Java8新特性二

Posted 钢铁-程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8新特性二相关的知识,希望对你有一定的参考价值。

文章目录

Java8新特性二

了解Fork/Join框架

Fork/Join框架:就是在必要的情况下,将一个大任务进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个小任务的结果进行join汇总。

Fork/Join框架与线程池的区别

采用“工作窃取”模式
当执行新的任务时,它可以将其拆分成更小的任务执行,并将小任务添加到线程队列中,当一个线程在执行任务时获取不到时,从一个随机的线程队列末尾偷一个并把它放在自己的队列中。

import java.util.concurrent.RecursiveTask;

//RecursiveTask有返回值
//RecursiveAcation没有返回值
//Recursive是递归的意思
public class ForkJoinCalculate extends RecursiveTask<Long> 

    private long start;
    private long end;
    private final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start, long end) 
        this.start = start;
        this.end = end;
    

    @Override
    protected Long compute() 
        long len = end -start;
        if(len<=THRESHOLD)
        
            long sum = 0;
            for(long i = start;i<=end;i++)
            
                sum+=i;
            
            return sum;
        
        else
        
            long middle = (start+end)/2;
            ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
            left.fork();//拆分子任务,同时压入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle+1,end);
            right.fork();
            return left.join()+right.join();
        

    


import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * @ClassName TestForkJoin
 * @Description: TODO
 * @Author renjie
 * @Date 2021/5/20
 **/
public class TestForkJoin 

    @Test
    public void test()
    
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinCalculate(0,100000000L);
        Long sum = pool.invoke(task);
        System.out.println(sum);

    


Java8并行流

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

/**
 * @ClassName TestForkJoin
 * @Description: TODO
 * @Author renjie
 * @Date 2021/5/20
 **/
public class TestForkJoin 
    /**
     * Java8并行流
     */
    @Test
    public void test1()
    
        Instant start = Instant.now();
        //串行流
        LongStream.rangeClosed(0,100000000000L)
                .reduce(0,Long::sum);
        Instant end = Instant.now();
        System.out.println("串行流耗费时间为:"+ Duration.between(start,end).toMillis());


        start = Instant.now();
        //并行流
        LongStream.rangeClosed(0,100000000000L)
                .parallel()
                .reduce(0,Long::sum);
        end = Instant.now();
        System.out.println("并行流耗费时间为:"+ Duration.between(start,end).toMillis());

    


输出

串行流耗费时间为:38956
并行流耗费时间为:13387

Optional类

Optional容器类的常用方法:

  • Optional.of(T t):创建一个Optional实例
  • Optional.empty():创建一个空的Optional实例
  • Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
  • isPresent():判断是否包含值
  • orElse(T t):如果调用对象包含值,返回该值,否则返回s获取的值。
  • map(Function f):如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map类似,要求返回值必须是Optional

示例

public class TestOptional 

    @Test
    public void test()
    
        Optional<Employee> op = Optional.of(new Employee());
        Employee emp = op.get();
        System.out.println(emp);
    

结果

Employeename='null', age=0, salary=0.0, status=null

例子2

public class TestOptional 
    @Test
    public void test1()
    
        Optional<Object> empty = Optional.empty();
        System.out.println(empty.get());
    

结果:抛出异常

java.util.NoSuchElementException: No value present

例子3

public class TestOptional 
	@Test
    public void test1()
    
        Optional<Object> empty = Optional.empty();
        //判断是否包含值
        if(empty.isPresent())
        
        	System.out.println(empty.get());
        
        else
        	System.out.println("啥都没有");
    

输出

啥都没有

例子4

有则获取,没有则替换

public class TestOptional 
    @Test
    public void test3()
    
        Optional<Employee> op = Optional.ofNullable(new Employee());
        Employee employee = op.orElse(new Employee("sss",20,1111.11,Employee.Status.BUSY));
        System.out.println(employee);
    

输出

Employeename='null', age=0, salary=0.0, status=null

例子5

public class TestOptional 
    @Test
    public void test4()
    
        Optional<Employee> op = Optional.ofNullable(new Employee("sss",20,1111.11,Employee.Status.BUSY));
        Optional<String> str = op.map(Employee::getName);
        System.out.println(str.get());
    

输出

sss

例子6

flatMap返回的必须是Optional 进一步避免空指针异常。

public class TestOptional 
    @Test
    public void test5()
    
        Optional<Employee> op = Optional.ofNullable(new Employee("sss",20,1111.11,Employee.Status.BUSY));
        Optional<String> str = op.flatMap((e)->Optional.of(e.getName()));
        System.out.println(str.get());
    

输出

sss

例子7

class NewMan 
    private Optional<Godness> godness = Optional.empty();

    public NewMan() 
    

    @Override
    public String toString() 
        return "NewMan" +
                "godness=" + godness +
                '';
    

    public Optional<Godness> getGodness() 
        return godness;
    

    public void setGodness(Optional<Godness> godness) 
        this.godness = godness;
    

    public NewMan(Optional<Godness> godness) 
        this.godness = godness;
    


public class TestOptional 
    @Test
    public void test6()
    
        Optional<NewMan> op = Optional.ofNullable(null);
        String str = getGodnessName(op);
        System.out.println(str);

    

    private String getGodnessName(Optional<NewMan> man) 
        return man.orElse(new NewMan())
                .getGodness()
                .orElse(new Godness("Miss Li"))
                .getName();
    


输出

Miss Li

接口中的默认方法与静态方法

以前接口中只可以有全局静态常量和抽象方法。
现在还可以有默认方法。使用default修饰符。

java8中允许接口包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用default关键词修饰。

也允许包含静态方法

例如:

public interface MyFunc<T> 

    T MyFunc(int a);
    default String getName()
    
        return "hello java8";
    
    static int getAge()
    
    	return 18;
    


接口默认方法的“类优先”原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时。

  • 选择父类中的方法。如果一个父类提供了一个具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突,如果一个父接口提供了一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认),那么必须覆盖该方法来解决冲突。

新时间API

之前的时间相关的类:

  • Date
  • Calendar:每周第一天默认从星期日开始,是可变的,线程不安全,格式化日期用SimpleDateFormat

新的时间API
不可变,不管怎么变都会产生新的实例。

  • 1、java.time下面的众多类,如LocalDate、LocalTime等,时间戳Instant
  • 2、时间日期格式化,java.time.format包下的类

旧API中SimpleDateFormat存在的线程安全问题

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @ClassName TestSimpleDateFormat
 * @Description: TODO
 * @Author renjie
 * @Date 2021/5/22
 **/
public class TestSimpleDateFormat 
    public static void main(String[] args) throws ExecutionException, InterruptedException 
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        Callable<Date> task = new Callable<Date>() 
            @Override
            public Date call() throws Exception 
                return sdf.parse("20161218");
            
        ;
        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<Date>> result = new ArrayList<>();
        for (int i = 0 ;i<10;i++) 

            result.add(pool.submit(task));
        

        for(Future<Date> future:result)
        
            System.out.println(future.get());
        
    

结果

Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
	at java.util.concurrent.FutureTask.report(FutureTask.java:122)
	at java.util.concurrent.FutureTask.get(FutureTask.java:192)
	at TestSimpleDateFormat.main(TestSimpleDateFormat.java:32)
Caused by: java.lang.NumberFormatException: multiple points

java.lang.NumberFormatException: multiple points问题
一般这种问题主要是因为SimpleDateFormat在多线程环境下,是线程不安全的,所以如果你在多线程环境中共享了SimpleDateFormat的实例,比如你在类似日期类中定义了一个全局的SimpleDateFormat对象,这样子肯定会出现上述的报错。

使用ThreadLocal进行改进

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

class DateFormatThreadLocal 
    public static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>()
        @Override
        protected DateFormat initialValue() 
            return new SimpleDateFormat("yyyyMMdd");
        
    ;
    public static Date convert(String source) throws ParseException 
        return df.get().parse(source);
    


public class TestDateFormatThreadLocal 

    public static void main(String[] args) throws ExecutionException, InterruptedException 
        Callable<Date> task = new Callable<Date>() 
            @Override
            public Date call() throws Exception 
                return DateFormatThreadLocal.convert("20161218");
            
        ;
        ExecutorService pool = Executors.newFixedThreadPool(10);

        List<Future<Date>> result = new ArrayList<>();
        for (int i = 0 ;i<10;i++) 

            result.add(pool.submit(task));
        

        for(Future<Date> future:result)
        
            System.out.println(future.get());
        

        pool.shutdown();
    

输出

Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016
Sun Dec 18 00:00:00 CST 2016

新时间日期API

LocalDate、LocalTime、LocalDateTime

使用LocalDate、LocalTime

以上是关于Java8新特性二的主要内容,如果未能解决你的问题,请参考以下文章

java8新特性

Java8新特性--Optional

JAVA8新特性——方法引用

java8新特性,使用流遍历集合

Java8新特性详解

java8新特性——Lambda表达式