7.1 Future任务机制与FutureTask
future提供: ① 判断任务是否完成;② 能够中断任务; ③ 能够获取任务执行结果;
futureTask: 构造
① public FutureTask(Callable
② public FutureTask(Runnable runnable, V result);创建一个任务,一旦运行就执行给定的Runnable,成功完成时返回给定结果;
7.2 Fork/Join框架
JDK7提供的用于并行执行任务的框架,把大任务分割成若干小任务,最终汇总每个小任务结果后得到大任务结果的框架;
ForkJoinTask与一般任务的主要区别在于它需要实现compute方法,这个方法中,首先需要判断任务是否够小,如果足够小就直接执行任务。如果不够小,就切分任务,每个子任务调用fork()时,再次进入compute(),再次判断是否需要进一步切分,如果不需要分割,则执行当前子任务并返回结果。使用join()会等待子任务执行完成并得到其结果。
ForkJoinTask 是实现Future的另一种有返回结果的实现,比Future多两个方法
fork() : 决定了ForkJoinTask 的异步执行,凭借这个方法可以创建新的任务。
Join() : 负责计算完成后返回结果,因此允许一个任务等待另一个任务执行完成。
ForkJoinTask需要通过ForkPool来执行,任务分割出的子任务会添加到当前工作线程锁维护的双端队列中,进入队列头部。
当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列尾部获取一个任务。
RecursiveAction: 继承ForkJoinTask,用于没有返回结果的任务。
RecursiveTask: 继承ForkJoinTask,用于有返回结果的任务。
ForkJoinPool: 和线程池ThreadPoolExecutor一样都是实现Executor接口。
ForkJoinPool 提供三个方法调度子任务:
Execute( ); 异步执行指定的任务;
Invoke( ); invokeAll( ); 执行指定的任务,等待完成,返回结果;
Submit( ); 异步执行指定的任务,并立即返回一个Future对象。
7.3 Fork/Join框架的实现原理
是一个特殊的线程池框架,专用于需要将一个任务不断分解,再不断汇总得到结果的计算过程。
实现了工作窃取算法:当一个线程空闲的时候,可以从其他线程的队列窃取任务来执行【每个线程应该从队列头部得到任务,而窃取线程将从其他线程的队列尾部得到任务】。
优点:充分利用线程进行并行运算,减少了线程之间的竞争
缺点:某些情况下还是存在竞争【比如双端队列只有一个任务时】;并且消耗了更多的系统资源【创建多个线程和多个双端队列】
ForkJoinPool由ForkJoinTask数组和ForkJoinWorkThread数组组成,
ForkJoinTask数组:负责存放程序提交给ForkPool的任务;
ForkJoinTask.fork( ); //调用该方法时,程序会调用push( ) 【把当前任务放在ForkJoinPool.WorkQueue[ ] 中】异步执行这个任务,然后立即返回结果。
ForkJoinTask.join( ); //主要作用:阻塞当前线程并等待获取结果。
① 调用doJoin( ) ,首先查看任务状态,是否已经执行完成,完成则直接返回任务状态;未完成则从任务数组中取出任务并执行;
如果任务顺利执行完成,设置为NORMAL;出现异常,设置为EXCEPTIONAL;
② externalAwaitDone( ) 方法中使用了同步代码块锁机制和interrupt阻塞机制;
③ 得到当前任务的状态来判断返回结果【NORMAL: 已完成; CANCELLED: 被取消; SIGNAL: 信号; EXCEPTIONAL: 异常;】
NORMAL: 直接返回结果;
CANCELLED: 直接抛出CancellationException;
EXCEPTIONAL: 直接抛出对应的异常;
ForkJoinWorkThread数组:负责执行任务;
7.4 异常处理
ForkJoinTask在执行的时候可能会抛出异常,但无法在主线程中直接捕获;
ForkJoinTask提供了 isCompletedAbnormally( ); 方法检查任务是否已经抛出异常或被取消,并且可以通过ForkJoinTask.getException( ); 获取异常;
ForkJoinTask.getException( ); 返回Throwable对象,如果任务被取消了,则返回CancellationException; 如果任务没有完成或者没有抛出异常,返回null;
7.5 优缺点
优点:
方便的利用多核平台的计算能力实现并发任务的拆分,极大的简化了编写并发程序的琐碎工作。
对于该模式下的应用,不再需要处理并行事务【同步、同信、死锁、data race…】。
缺点:
需要注意,如果拆分对象过多,短时间内将内存撑满。等待线程的CPU资源释放了,但线程对象等待时不会被垃圾回收机制回收;