使用执行器无需创建和管理线程,只要通过实现Runnable或Callable接口创建任务,并将任务发送给执行器,其线程池会分配一个线程来执行任务。
Java 7引入fork/join框架的执行器,此框架在ForkJoinPool类中实现,使用分治技术将问题分割成更小的部分。当为fork/join框架实现任务时,需要检查待解决问题的大小。如果大于预定义值,则将问题划分为两个或多个子集,并创建与所划分数量相同的子任务。任务使用fork()操作发送这些子任务到ForkJoinPool类,并使用join()操作等待任务结束。
对于这类问题,fork/join池的性能要优于传统执行器。本节将实现范例来验证这个观点。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤实现范例:
-
创建名为TaskFJ的类,指定其继承RecursiveAction类:
public class TaskFJ extends RecursiveAction{
-
声明名为array的私有整型数组:
private final int array[];
-
声明名为start和end的私有整型属性:
private final int start, end;
-
实现类构造函数,初始化属性:
public TaskFJ(int array[], int start, int end) { this.array=array; this.start=start; this.end=end; }
-
实现compute()方法。如果此任务需要处理超过1000个元素(由开始和结束属性决定)的块,则创建两个
TaskFJ对象,使用fork()方法将它们发送到ForkJoinPool类,使用join()方法等待任务执行结束:
@Override protected void compute() { if (end-start>1000) { int mid=(start+end)/2; TaskFJ task1=new TaskFJ(array,start,mid); TaskFJ task2=new TaskFJ(array,mid,end); task1.fork(); task2.fork(); task1.join(); task2.join();
-
反之,递增此任务需要处理的元素。每次递增操作之后,设置线程休眠1毫秒:
} else {
for (int i=start; i<end; i++) {
array[i]++;
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
-
创建名为Task的类,指定其实现Runnable接口:
public class Task implements Runnable{
-
声明名为array的私有整型数组:
private final int array[];
-
实现类构造函数,初始化属性:
public Task(int array[]) { this.array=array; }
-
实现run()方法。递增数组中的所有元素,每次递增操作之后,设置线程休眠1毫秒:
@Override public void run() { for (int i=0; i<array.length; i++ ){ array[i]++; try { TimeUnit.MILLISECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
实现本范例主类,创建名为Main的类,包含main()方法:
public class Main { public static void main(String[] args) {
-
创建包含100000个元素的整型数组:
int array[]=new int[100000];
-
创建Task对象和ThreadPoolExecutor对象,然后执行。通过控制任务运行的时间来执行任务:
Task task=new Task(array); ExecutorService executor=Executors.newCachedThreadPool(); Date start,end; start=new Date(); executor.execute(task); executor.shutdown(); try { executor.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); } end=new Date(); System.out.printf("Main: Executor: %d\n", (end.getTime()-start.getTime()));
-
创建TaskFJ对象和ThreadPoolExecutor对象,然后执行。通过控制任务运行的时间来执行任务:
TaskFJ taskFJ=new TaskFJ(array,1,100000); ForkJoinPool pool=new ForkJoinPool(); start=new Date(); pool.execute(taskFJ); pool.shutdown(); try { pool.awaitTermination(1, TimeUnit.DAYS); } catch (InterruptedException e) { e.printStackTrace(); } end=new Date(); System.out.printf("Core: Fork/Join: %d\n", (end.getTime()-start.getTime())); } }
工作原理
当执行本范例时,会看到ForkJoinPool和TaskFJ类的性能是如何优于ThreadPoolExecutor和Task类的。
如果需要解决能够使用分治技术进行分割的问题,使用ForkJoinPool类代替ThreadPoolExecutor类,性能会更好。
扩展学习
- 本章“将线程管理委托给执行器”小节