最近在看线程这一块的东西,所以之前的那篇文章就是用来记录的,但看起来好简单的样子,哈哈哈!
这两天看的是Fork/Join 分而治之的思想,Doug Lea大师的JUC还是挺强的,学并发编程应该没有人不知道这个大佬吧!
上篇少记录了一个东西,叫守护线程,即Thread.setDaemon(boolean on);当参数为false的时候,没有意思,当参数为true的时候,即调用该方法的线程与当前的线程共生死,就是当前线程结束了后无论调用setDeamon(true)的线程是否结束,它都会结束;这个这么看起来虽然用处不大,但是在使用单元测试的时候,即junit测试,使用@Test进行测试线程时,当测试线程走完后,调用的线程都会结束,就是因为守护线程的存在,导致会出现这样的现象;所以在测试线程的时候尽量不要使用junit单元测试,还是使用main方法测试比较好。
现在开始谈谈Fork/Join啦,分而治之,就是将一个大任务拆分到多个小任务,然后小任务同时执行,执行完后将每个小任务运算结果进行合并,这样能够加快程序运行的速度。像这种分而治之的思想在归并排序算法中能体现出来。
Fork/Join的使用标准范式:
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask task = new ForkJoinTask();
pool.invoke(task);
Result = task.join();
ForkJoinPool其实是个线程池继承自JDK的线程池ExecutorService,说白就是多线程执行多个任务,然后将结果进行合并预算,来提升程序的运行的速度
ForkJoinTask主要有两个常用的实现类RecursiveAction以及RecursiveTask<T> 即一个是同步带有返回值的,后面那个异步不带有返回值的
RecursiveTask<T>的使用示例:
class UseRecursiveTask extends RecursiveTask<T> {protected T compute(){//do something...UseRecursiveTask task1 = new UseRecursiveTask();UseRecursiveTask task2 = new UseRecursiveTask();invokeAll(task1,task2);//再次调用task1和task2的compute()方法return task1.join() + task2.join();//阻塞并等待获取结果值}
}
//主方法
public static void main(String [] arg0){ForkJoinPool pool = new ForkJoinPool();UseRecursiveTask task = new UseRecursiveTask<T>();pool.invoke(task);T result = task.join();//开始执行,进入阻塞,等待拆分任务全部执行。//do something...
}
RecursiveAction的使用示例:
class UseRecursiveAction extends RecursiveAction {protected void compute(){//do something...UseRecursiveAction action1 = new UseRecursiveAction();UseRecursiveAction action2 = new UseRecursiveAction();List<UseRecursiveAction> list = new ArrayList<UseRecursiveAction>();list.add(action1);list.add(action2);Collection<UseRecursiveAction> collection = invokeAll(list);for(UseRecursiveAction action : collection){action.join();//异步执行}}
}
//主线程
public static void main(String [] arg0){ForkJoinPool pool = new ForkJoinPool();UseRecursiveAction action = new UseRecursiveAction();pool.execute(action);//开始异步执行//这里可执行其他业务逻辑//....action.join();//进入阻塞状态,等待异步线程执行完毕
}
上面就是两种示例,在Fork/Join中其实还有一种思想叫工作密取(Worker-Stealing),就是空闲的线程会从繁忙线程的任务队列中,从任务队列后面获取一个任务去执行,在ForkJoinPool 中就已经实现好了。
既然谈到了这个,就简单的看了一下源代码,在ForkJoinPool 类中有个内部类叫WorkQueue,维护了一个它的数组,用来储存每个任务的,看了一下它的源代码,感觉有点艰难,还是等下次变得更强的时候再研究吧!