使用fork/join框架代替执行器

Java 9并发编程指南 目录

使用fork/join框架代替执行器

使用执行器无需创建和管理线程,只要通过实现Runnable或Callable接口创建任务,并将任务发送给执行器,其线程池会分配一个线程来执行任务。

Java 7引入fork/join框架的执行器,此框架在ForkJoinPool类中实现,使用分治技术将问题分割成更小的部分。当为fork/join框架实现任务时,需要检查待解决问题的大小。如果大于预定义值,则将问题划分为两个或多个子集,并创建与所划分数量相同的子任务。任务使用fork()操作发送这些子任务到ForkJoinPool类,并使用join()操作等待任务结束。

对于这类问题,fork/join池的性能要优于传统执行器。本节将实现范例来验证这个观点。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤实现范例:

  1. 创建名为TaskFJ的类,指定其继承RecursiveAction类:

    public class TaskFJ extends RecursiveAction{
    
  2. 声明名为array的私有整型数组:

    	private final int array[];
    
  3. 声明名为start和end的私有整型属性:

    	private final int start, end;
    
  4. 实现类构造函数,初始化属性:

    	public TaskFJ(int array[], int start, int end) {
    		this.array=array;
    		this.start=start;
    		this.end=end;
    	}
    
  5. 实现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();
    
  6. 反之,递增此任务需要处理的元素。每次递增操作之后,设置线程休眠1毫秒:

		} else {
			for (int i=start; i<end; i++) {
				array[i]++;
				try {
					TimeUnit.MILLISECONDS.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}		
	}
}
  1. 创建名为Task的类,指定其实现Runnable接口:

    public class Task implements Runnable{
    
  2. 声明名为array的私有整型数组:

	private final int array[];
  1. 实现类构造函数,初始化属性:

    	public Task(int array[]) {
    		this.array=array;
    	}
    
  2. 实现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();
    			}
    		}
    	}
    }
    
  3. 实现本范例主类,创建名为Main的类,包含main()方法:

    public class Main {
    	public static void main(String[] args) {
    
  4. 创建包含100000个元素的整型数组:

    		int array[]=new int[100000];
    
  5. 创建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()));
    
  6. 创建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类,性能会更好。

扩展学习

  • 本章“将线程管理委托给执行器”小节
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值