Fork/Join框架
java7提供的一个用于并行执行的框架,是一个把大任务拆分成若干个小任务,然后把小任务的结果汇总得到大任务结果的框架。
- 工作窃取算法
- 定义:指某个线程从其他队列里窃取任务来执行。
因为fork会将大任务分成若干个小的任务,然后为了减少线程之间的竞争,会将这些子任务放入到不同的队列中,并为每个队列创建一个单独的线程来执行。当某线程的队列任务执行完时,可以去其他队列里窃取任务来处理。
为了减少窃取任务线程和被窃取任务的线程之间的线程,任务队列一般采用双端队列的设计,被窃取任务线程从队首提取任务,而窃取任务的线程从队尾提取任务。 - 优点:充分利用线程的并行计算,减少线程之间的竞争
- 缺点:某些情况下还是会存在线程竞争,比如,队列里只有一个任务的时候。并且,因为创建多个线程和多个双端队列,所以资源开销比较大。
- 定义:指某个线程从其他队列里窃取任务来执行。
- Fork/Join框架的设计
设计步骤:
1. 任务分割:利用Fork类来将大任务迭代切分成足够小的子任务。
2. 执行子任务并合并结果:分割后的子任务分别存储在双端队列的头部,对应线程分别从队列里提取任务并执行。执行完的结果统一放置在一个队列里。启动一个线程,从该队列里提取数据并进行合并。
设计实现:
1. ForkJoinTask类:ForkJoin任务,通常情况下通过继承该类的子类RecursiveAction(没有返回结果的任务)和RecursiveTask(用于有返回结果的任务)来创建一个ForkJoin任务,来提供在任务中执行fork()和join()操作的机制。ForkJoinTask需要实现compute()方法,在该方法里需要对人物进行判断分割和计算合并。
2. ForkJoinPool:ForkJoinTask任务需要通过ForkJoinPool来执行。该ForkJoinPool是由ForkJoinTask数组和ForkJoinWorkerThread数组组成,前者负责存放程序提交给ForkJoinPool的任务,而后者负责执行这些任务。
实现原理:- ForkJoinTask的fork()方法:当我们调用该方法时,程序会调用ForkJoinWorkerThread的pushTask方法异步地执行这个任务,然后立即返回结果。
pushTask方法经当前任务存放在ForkJoinTask数组里,然后调用ForkJoinPool的signalWork()方法来唤醒或者创建一个工作线程来执行任务。 - ForkJoinTask的join()方法:主要的作用就是阻塞当前线程并等待获取结果。它首先调用doJoin()方法来得到当前任务的状态来判断返回什么结果。任务状态有四种:已完成Normal,被取消Cancelled,信号Signal和出现异常Exception。
- ForkJoinTask的fork()方法:当我们调用该方法时,程序会调用ForkJoinWorkerThread的pushTask方法异步地执行这个任务,然后立即返回结果。
- Fork/Join框架的异常处理
join方法在处理的时候:首先查看任务的状态,如果执行完成,则直接返回任务状态;如果没有执行完,则从任务数组里取出任务并执行。如果任务顺利执行完成,则设置任务状态为NORMAL,如果出现异常,则记录异常,并将任务状态设置为EXCEPTIONAL。
因为ForkJoinTask执行时候抛出的异常不能在主线程中捕获,因此提供了isCompletedAbnormally()方法来检查任务是否已经抛出异常或者被取消:if(task.isCompletedAbnormally()) { System.out.println(task.getException()); }
并且通过getException()方法获取异常,如果没有处理完成或者正在处理,返回null,如果已经被取消,返回CancellationException。