实现为fork/join框架生成自定义线程的ThreadFactory接口

Java 9并发编程指南 目录

实现为fork/join框架生成自定义线程的ThreadFactory接口

fork/join框架是Java9中最有趣的特性之一,它是Executor和ExecutorService接口的实现,能够直接执行Callable和Runnable任务,无需管理执行它们的线程。

此执行器目标是执行可以划分为更小部分的任务,其主要组成部分如下:

  • 由ForkJoinTask类实现的特殊任务。
  • 提供两个操作,用于将任务划分为子任务(fork操作)和等待这些子任务结束(join操作)。
  • 它是一种命名为“工作窃取”的算法,优化线程池的使用。当任务等待其子任务时,正在执行此任务的线程会去运行其它任务。

fork/join框架的主类是ForkJoinPool类。在内部包括如下两个元素:

  • 等待被执行的任务队列
  • 执行任务的线程池

ForkJoinWorkerThread向Thread类添加新方法,比如创建线程时执行的onStart()方法和清理线程使用资源时调用的onTermination()方法。ForkJoinPool类使用ForkJoinWorkerThreadFactory接口的实现来创建其使用的工作线程。

本节将学习如何实现在ForkJoinPool类中使用的定制工作线程,以及在继承ForkJoinPool类和实现ForkJoinWorkerThreadFactory接口的工厂中如何使用此线程。

准备工作

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

实现过程

通过如下步骤实现范例:

  1. 创建名为MyWorkerThread的类,继承ForkJoinWorkerThread类:

    public class MyWorkerThread extends ForkJoinWorkerThread{
    
  2. 声明和创建ThreadLocal属性,由名为taskCounter的Integer类参数化:

    	private final static ThreadLocal<Integer> taskCounter= new ThreadLocal<Integer>();
    
  3. 实现类构造函数:

    	protected MyWorkerThread(ForkJoinPool pool) {
    		super(pool);
    	}
    
  4. 重写onStart()方法,在其父类上调用此方法输出信息到控制条,且设置这个线程的taskCounter属性值为零:

    	@Override
    	protected void onStart() {
    		super.onStart();
    		System.out.printf("MyWorkerThread %d: Initializing task counter.\n", getId());	
    		taskCounter.set(0);
    	}
    
  5. 重写onTermination()方法,输出此线程的taskCounter属性值到控制台:

    	@Override
    	protected void onTermination(Throwable exception) {
    		System.out.printf("MyWorkerThread %d: %d\n",
    		getId(),taskCounter.get());
    		super.onTermination(exception);
    	}
    
  6. 实现addTask()方法,递增taskCounter属性值:

    	public void addTask(){
    		taskCounter.set(taskCounter.get() + 1);;
    	}
    }
    
  7. 创建名为MyWorkerThreadFactory的类,实现ForkJoinWorkerThreadFactory接口。实现newThread()方法,创建和返回MyWorkerThread对象:

    public class MyWorkerThreadFactory  implements ForkJoinWorkerThreadFactory{
    	@Override
    	public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
    		return new MyWorkerThread(pool);
    	}
    }
    
  8. 创建名为MyRecursiveTask的类,继承Integer类参数化的RecursiveTask类:

    public class MyRecursiveTask extends RecursiveTask<Integer> {
    
  9. 定义名为array的私有int数组:

    	private int array[];
    
  10. 定义名为start和end的两个私有int属性:

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

    	public MyRecursiveTask(int array[],int start, int end) {
    		this.array=array;
    		this.start=start;
    		this.end=end;
    	}
    
  12. 实现compute()方法,将数组中开始和结束位置之间所有元素相加。首先将正在执行任务的线程转换成MyWorkerThread对象,并且使用addTask()为此线程递增任务的计数:

    	@Override
    	protected Integer compute() {
    		Integer ret;
    		MyWorkerThread thread=(MyWorkerThread)Thread.currentThread();
    		thread.addTask();
    
  13. 如果数组中开始和结束位置区间的元素数大于100,计算中间位置且创建两个新的MyRecursiveTask任务分别处理前后两部分。如果区间等于或小于100,计算开始和结束位置区间所有元素的和:

    		if (end-start>100) {
    			int mid=(start+end)/2;
    			MyRecursiveTask task1=new MyRecursiveTask(array,start,mid);
    			MyRecursiveTask task2=new MyRecursiveTask(array,mid,end);
    			invokeAll(task1,task2);
    			ret=addResults(task1,task2);
    		} else {
    			int add=0;
    			for (int i=start; i<end; i++) {
    				add+=array[i];
    			}
    			ret=add;
    		}
    
  14. 设置线程休眠10毫秒,返回任务结果:

    		try {
    			TimeUnit.MILLISECONDS.sleep(10);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    		return ret;
    	}
    
  15. 实现addResults()接口,计算并返回作为参数接收的两个任务的结果之和:

    	private Integer addResults(MyRecursiveTask task1, MyRecursiveTask task2) {
    		int value;
    		try {
    			value = task1.get().intValue()+task2.get().intValue();
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    			value=0;
    		} catch (Exception e) {
    			e.printStackTrace();
    			value=0;
    		}	
    		return value;
    	}
    }
    
  16. 通过创建名为Main的类,添加main()方法,实现本范例主类:

    public class Main {
    	public static void main(String[] args)  throws Exception{
    
  17. 创建名为factory的MyWorkerThreadFactory对象:

    		MyWorkerThreadFactory factory=new MyWorkerThreadFactory();
    
  18. 创建名为pool的ForkJoinPool对象,传递之前创建的factory对象到构造函数:

    		ForkJoinPool pool=new ForkJoinPool(4, factory, null, false);
    
  19. 创建100000个整数的数组,初始化所有元素为1:

    		int array[]=new int[100000];
    		for (int i=0; i<array.length; i++){
    			array[i]=1;
    		}
    
  20. 创建新的task对象,计算数组中所有元素的和:

    		MyRecursiveTask task=new MyRecursiveTask(array,0,array.length);
    
  21. 使用execute()方法发送任务到线程池:

    		pool.execute(task);
    
  22. 使用join()方法等待任务结束:

    		task.join();
    
  23. 使用shutdown()方法关闭线程池:

    		pool.shutdown();
    
  24. 使用awaitTermination()方法等待执行器结束:

    		pool.awaitTermination(1, TimeUnit.DAYS);
    
  25. 使用get()方法,输出任务结果到控制台:

    		System.out.printf("Main: Result: %d\n",task.get());
    
  26. 输出指明程序结束的信息到控制台:

    		System.out.printf("Main: End of the program\n");
    	}
    }
    

工作原理

fork/join框架使用的线程称为工作线程。 Java提供ForkJoinWorkerThread类,此类继承了Thread类并实现fork/join框架使用的工作线程。

本节实现了继承ForkJoinWorkerThread类的MyWorkerThread类,并重写ForkJoinWorkerThread类的两个方法。范例目标是在每个工作线程中实现任务计数器,以便了解一个工作线程已经执行多少任务。通过ThreadLocal属性实现计数器,这样每个线程都将以透明的方式拥有自己的计数器。

重写ForkJoinWorkerThread类的onStart()方法初始化任务计数器,当工作线程开始执行时调用此方法。还重写了onTermination()方法输出任务计数器的值到控制台,当工作线程结束执行时调用此方法。此外,在MyWorkerThread类中实现了addTask()方法,用来递增每个线程的任务计数器。

ForkJoinPool类像Java并发API中所有执行器一样,使用工厂来创建线程。所以如果在ForkJoinPool类中使用MyWorkerThread线程,需要实现自定义的线程工厂。对于fork/join框架框架,这个工厂需要实现ForkJoinPool.ForkJoinWorkerThreadFactory类,此类只有一个方法来创建新的MyWorkerThread对象。

最后,只要使用已经创建的工厂初始化ForkJoinPool类,也就是在Main类中所做的,使用ForkJoinPool类构造函数。

下图显示本范例在控制台输出的部分执行信息:

pics/08_05.jpg

可以看到ForkJoinPool对象如何执行四个工作线程,以及每个线程执行多少个任务。

扩展学习

需要注意当线程正常结束或者抛出异常时,才会调用ForkJoinWorkerThread类提供的onTermination()方法。此方法将Throwable对象作为参数接收,如果参数值为null,工作线程正常结束,但如果参数值不为空,线程抛出异常,所以需要编写必要的程序来处理这种情形。

更多关注

  • 第五章“Fork/Join框架”中的“创建fork/join池”小节
  • 第一章“线程管理”中的“工厂模式创建线程”小节
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值