ForkJoin 拆分任务
概述
本章节用一个简单示例说明如何拆分任务。示例是更新一批产品的价格。任务类继承ForkJoinTask类,并复写execute方法,在execute方法中进行任务拆分或者价格更新。如果任务需要更新的产品数量大于10,它将拆分这些元素为两部分,并相应创建两个任务,再通过调用invokeAll方法来分配所拆分的任务,invokeAll方法是一个同步调用,这个任务将等待子任务完成,然后继续执行。当一个主任务等待它的子任务时,执行这个主任务的工作者线程接受另一个等待执行的任务并开始执行。正因为这个行为,Fork/Join框架提供了比Runnable和Callable更加高效的任务管理机制。ForkJoinTask类的invokeALl方法是执行器框架和Fork/Join框架的主要差异之一。在执行器框架中,所有的任务必须发送给执行器;反而,在下面的示例中,线程池包含了待执行方法的任务,也包含了任务的控制。
ForkJoinPool也需要通过shutdown方法来关闭。
示例代码如下:
public class ForkDemo {
public static void main(String[] args){
//生成产品数据
System.out.println("main:生成产品数据");
List<Product> products = new ArrayList<Product>(10000);
for(int i=0; i<10000; i++){
Product p = new Product();
p.setPrice(10);
products.add(p);
}
//创建TaskDemo对象来更新产品价格
TaskDemo task = new TaskDemo(products,0,products.size());
//创建ForkJoinPool对象,并启动
System.out.println("main:创建ForkJoinPool对象,并启动");
ForkJoinPool pool = new ForkJoinPool();
pool.execute(task);
System.out.println("main:输出线程池信息");
do{
System.out.println("main:线程数量:" + pool.getActiveThreadCount());
System.out.println("main:偷窃数量:" + pool.getStealCount());
System.out.println("main:并行级别:" + pool.getParallelism());
try {
TimeUnit.MICROSECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while(!task.isDone());
//关闭线程池
System.out.println("main:关闭线程池");
pool.shutdown();
if(task.isCompletedNormally()){
System.out.println("main:任务处理完成");
}
System.out.println("main:退出");
}
}
class Product {
private double price;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
/**
* 采用分治算法对产品价格进行调整
*
* 如果一次计算的元素个数大于10,则继续拆分;否则进行产品价格更新
*/
class TaskDemo extends RecursiveAction{
private List<Product> products;
private int first;
private int last;
TaskDemo(List<Product> products, int first, int last) {
this.products = products;
this.first = first;
this.last = last;
}
@Override
protected void compute() {
if(last-first < 10){
//如果小于10个元素,则更新价格
updatePrice();
}else{
//如果大于10个元素,则进行拆分
int middle = (last+first)/2;
TaskDemo t1 = new TaskDemo(products,first,middle+1);
TaskDemo t2 = new TaskDemo(products,middle+1,last);
invokeAll(t1,t2);
}
}
/** 更新价格为20 **/
private void updatePrice(){
for(int i=first;i<last; i++){
products.get(i).setPrice(20);
}
}
}
程序运行日志:
main:生成产品数据
main:创建ForkJoinPool对象,并启动
main:输出线程池信息
main:线程数量:1
main:偷窃数量:0
main:并行级别:2
main:线程数量:2
main:偷窃数量:2
main:并行级别:2
main:关闭线程池
main:任务处理完成
main:退出