thread_fork/join并发框架2
转自 http://blog.csdn.net/mr_zhuqiang/article/details/48300229
三.使用异步方式
invokeAll(task1,task2); 是同步方式,将当前任务挂起直到子任务发送到 Fork/join线程池中执行完成,这种方式允许工作窃取算法,分配一个新任务给在执行休眠任务的工作者线程。
相反当采用异步的方式(比如,fork()),任务将继续执行,所以就没有办法使用工作窃取算法了。因为不存在等待的线程了。除非使用join() 或则 get() 来获取结果,等待任务的完成。
invokeAll()采用同步方式,工作者线程将会休眠等待子任务的完成,所以能使用窃取算法派给工作者一个新任务
fork() 采用异步方式,只有结合join()或者get() 来等待任务的完成,进而可以使用窃取算法来提高性能。
get():如果ForkJoinTask类执行结束,或则一直等到结束,那么get()方法的这个版本则返回由compute()方法返回的结果
get() 方法 和 join()方法的区别:
join()方法不能被中断,如果中断join()方法的线程,方法将抛出Interrupted异常
如果任务抛出任何运行时异常,那么get()方法将返回ExecutionException异常,但是join方法返回的是RuntimeException;
public class ForkJoin3Test {
public static void main(String[] args) throws InterruptedException {
ForkJoinPool pool = new ForkJoinPool();
Task3 mp3 = new Task3("C:\\360CloudUI", "mp3");
pool.execute(mp3); do {
System.out.println("*********** 状态信息巡查 ***************");
System.out.printf("最大并行任务:%s,当前活动任务数(不准确的):%s,队列中的任务数量:%s,窃取数量:%s\n",
pool.getParallelism(),
pool.getActiveThreadCount(),
pool.getQueuedTaskCount(),
pool.getStealCount());
TimeUnit.MILLISECONDS.sleep(10);
} while (!mp3.isDone()); // 未完成则一直循环获取状态信息 pool.shutdown();
List<String> join = mp3.join();
System.out.println("共找到符合的文件数量:" + join.size());
for (String s : join) {
System.out.println(s);
}
}
} class Task3 extends RecursiveTask<List<String>> {
private static final long serialVersionUID = 1L;
private String path; // 文件夹路径
private String suffix; // 后缀 public Task3(String path, String suffix) {
this.path = path;
this.suffix = suffix;
}
@Override
protected List<String> compute() {
List<String> result = new ArrayList<String>(); // 存储结果
List<Task3> tasks = new ArrayList<Task3>(); // 存储任务 File file = new File(path);
File[] files = file.listFiles();
for (File f : files) { // 分发和执行任务
if (f.isDirectory()) { // 如果是文件夹,则使用异步的方式发送一个任务去执行
Task3 task = new Task3(f.getAbsolutePath(), suffix);
task.fork(); // 拆分任务异步执行
tasks.add(task);
} else {
String name = f.getName();
if (name.endsWith(suffix)) {
result.add(name);
}
}
}
if (tasks.size() > 1) { // 如果当前任务大于1个 则打印信息
System.out.printf("%s,tasks size(当前路径有) = %s个(文件夹),当前路径是:%s\n", Thread.currentThread().getName(),
tasks.size(), path);
}
for (Task3 task : tasks) { // 获取当前任务的结果
List<String> join = task.join(); // 调用join方法等待任务完成
result.addAll(join); // 把任务结果添加到当前任务的结果中
}
return result;
}
}
四 取消任务
ForkJoinTask 对象中有一个cancel()方法来取消未开始的任务。取消任务有以下两点需要注意:
1. ForkJoinPool类不提供任何方法来取消线程池中正在运行或则等待运行的所有任务。
2. 取消任务时,不能取消已经被执行的任务。
public class ForkJoin4Test {
public static void main(String[] args) throws InterruptedException, ExecutionException {
int[] arrs = new ArrayGenerator().generateArray(50);
//
TaskManger taskManger = new TaskManger();
ForkJoinPool pool = new ForkJoinPool();
SearchNumberTask task = new SearchNumberTask(arrs, 0, arrs.length, 50, taskManger); pool.execute(task);
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MILLISECONDS); // System.out.println("main:结束:" + task.get());
}
} // 数组生成
class ArrayGenerator {
public int[] generateArray(int size) {
int[] array = new int[size];
Random random = new Random();
for (int i = 0; i < size; i++) {
array[i] = random.nextInt(10);
}
return array;
}
} // 任务管理类
class TaskManger {
private List<ForkJoinTask<Integer>> tasks = new ArrayList<ForkJoinTask<Integer>>(); public void addTask(ForkJoinTask<Integer> task) {
tasks.add(task);
} public void cancelTasks(ForkJoinTask<Integer> cancelTask) {
for (ForkJoinTask<Integer> task : tasks) {
if (task != cancelTask) {
task.cancel(true);
((SearchNumberTask) task).writeCanceMesg();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class SearchNumberTask extends RecursiveTask<Integer> {
private int[] numbers;
private int start, end;
private int number;
private TaskManger taskManger; private final static int NOT_FOUND = -1; public SearchNumberTask(int[] numbers, int start, int end, int number, TaskManger taskManger) {
this.numbers = numbers;
this.start = start;
this.end = end;
this.number = number;
this.taskManger = taskManger;
} @Override
protected Integer compute() { int ret;
if (end - start > 10) { // 拆分任务 ret = launchTasks(); } else { // 执行查找
System.out.println("Task:开始:" + start + ":" + end);
ret = lookForNumber();
System.out.println("Task:结束--------:" + start + ":" + end);
} return ret;
} /**
* 查找数字
*
* @return
*/
private int lookForNumber() {
for (int i = start; i < end; i++) {
if (numbers[i] == number) {
System.out.printf("Task:目标number:%s已被找到,索引位置:%s\n", number, i);
taskManger.cancelTasks(this);
return i;
}
}
return NOT_FOUND;
} /**
* 拆分任务
*
* @return
*/
private int launchTasks() {
int mid = (start + end) / 2;
SearchNumberTask task1 = new SearchNumberTask(numbers, start, mid, number, taskManger);
SearchNumberTask task2 = new SearchNumberTask(numbers, mid, end, number, taskManger);
taskManger.addTask(task1);
taskManger.addTask(task2);
task1.fork(); // 异步执行
task2.fork(); int result = task1.join();
if (result != -1) {
return result;
}
return task2.join();
} /** 取消任务 信息 **/
public void writeCanceMesg() {
System.out.printf("Task:取消了,start=%s,end=%s\n", start, end);
}
}
五 运行异常
Java有两种类型的异常:
非运行时异常(Checked Exception):必须在方法上通过throws 子句抛出,或则通过try…catch语句进行扑捉处理。
运行时异常(Unchecked Exception):不是强制的需要捕捉处理和throws抛出。
在ForkJoinTask类的compute方法中不能抛出非运行时异常,因为该方法没有throws的声明,根据Java重新方法的规则,所以不能抛出。而且在该compute中抛出的运行时异常,给我最明显直观的结果是,只要不调用get()获取结果,控制台是不会打印异常信息的。也就是说,异常被吞噬了。但是我们可以通过该类的其他方法来获取该异常。
task.isCompletedNormally() : 任务完成时没有出错
task.isCompletedAbnormally() : 来检查任务是否已经抛出异常或已经被取消了,要注意此方法。由于提交任务之后,检测该任务是否有异常,不是阻塞的。所以需要等待任务的完成。才能正确的获取到是否有异常
task.getException() : 获得任务中抛出的异常
该类中抛出的异常,只要一抛出异常,子任务都不会再继续执行。(反正就是说只要抛出了异常,任务结果肯定是不正确的了)
completeExceptionally(Throwable ex) : 该方法 可以在语义上抛出一个异常,包括非运行时异常。要在获取结果前 通过task.isCompletedAbnormally()来配合操作。
public class ForkJoin5Test {
public static void main(String[] args) throws InterruptedException {
int[] arrs = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ForkJoinPool pool = new ForkJoinPool(1);
Task5 task = new Task5(arrs, 0, arrs.length);
pool.execute(task); pool.shutdown(); // 关闭执行器,并配合超时。来等待任务运行完成,发现一个特性:该执行器如来里面没有可活动的任务。执行器会自动关闭。而且调用get会阻塞任务直到返回结果
pool.awaitTermination(1, TimeUnit.DAYS); // task.isCompletedNormally() 任务完成时没有出错
if (task.isCompletedAbnormally()) { // 来检查任务是否已经抛出异常或已经被取消了,要注意此方法。由于提交任务之后,检测该任务是否有异常,不是阻塞的。所以需要上面的等待任务的完成。才能正确的获取到是否有异常
System.out.println("检测到任务中有抛出的异常:" + task.getException().getMessage());
} else {
System.out.println(task.join());
}
}
} class Task5 extends RecursiveTask<Integer> {
private int[] arrs; // 要处理的数据
private int start; // 开始索引
private int end; // 结束索引 public Task5(int[] arrs, int start, int end) {
this.arrs = arrs;
this.start = start;
this.end = end;
} @Override
protected Integer compute() {
int result = 0;
if (end - start < 2) {
for (int i = start; i < end; i++) {
result += arrs[i];
}
System.out.printf("%s,结果:%s\n", Thread.currentThread().getName(), result);
return result;
} else {
int mid = (start + end) / 2;
// System.out.println(mid);
if (mid == 2) {
throw new RuntimeException("故意抛出的测试异常"); // 为了测试抛出异常,可以
// 关闭测异常。运行查看结果
// Exception e = new Exception("故意抛出的非运行时异常");
// completeExceptionally(e); //也可以使用 该方法,设置一个异常,因为 源码
// setExceptionalCompletion
// 是设置的异常,就相当于该异常并没有被抛出。在语义上通过task.isCompletedAbnormally()来抛出了非运行时异常
// return null; // 如果不返回,程序将继续执行后面的代码,并不能达到真正抛出异常的效果
}
// 拆分成2个子任务继续检测和执行
Task5 task1 = new Task5(arrs, start, mid);
Task5 task2 = new Task5(arrs, mid, end);
invokeAll(task1, task2); // 使用同步的方式 执行
try {
result = task1.get() + task2.get(); // 把子任务返回的结果相加
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return result;
}
}
thread_fork/join并发框架2的更多相关文章
- thread_fork/join并发框架1
一.线程并发控制 Thread.Executor.ForkJoin和Actor 1.裸线程 Runnable接口,调用.start()开始,没有现成的API来结束线程,你需要自己来实现, ...
- Java 7 Fork/Join 并行计算框架概览
应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在 很多的应用程序在运行在多核 ...
- Java 并发系列之十:java 并发框架(2个)
1. Fork/Join框架 2. Executor框架 3. ThreadPoolExecutor 4. ScheduledThreadPoolExecutor 5. FutureTask 6. t ...
- 深入理解Java并发框架AQS系列(一):线程
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...
- 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)
一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...
- 协程并发框架gevent及其用法
gevent是python的一个并发框架,采用协程实现并发目的,用起来也非常简单 gevent的docs:http://www.gevent.org/contents.html 一个最简单的例子: i ...
- Python 开源异步并发框架的未来
http://segmentfault.com/a/1190000000471602 开源 Python 是开源的,介绍的这几个框架 Twisted.Tornado.Gevent 和 tulip 也都 ...
- Python开源异步并发框架
Python开源异步并发框架的未来 2014年3月30日,由全球最大的中文IT社区CSDN主办的“开源技术大会·” (Open Source Technology Conference ,简称OSTC ...
- J.U.C并发框架
转载:http://itindex.net/detail/48869-j.u.c-%E6%A1%86%E6%9E%B6 J.U.C并发框架 作者:Doug Lea SUNY Oswego Oswego ...
随机推荐
- 定时从远程的数据库中取数据,然后把取出来的数据插入或更新本地的oracle数据库的表
最近项目中有一种需求: 大致需求是这样的 通过给定的 用户名和密码 要定时从远程的数据库中取数据,然后把取出来的数据插入或更新本地的oracle数据库的表 项目的结构式struts1 hibernat ...
- IOS从一个APP跳到另一个APP
以下为跳转到大众点评APP代码如下: NSString *requestUrlString = @"dianping://shopinfo?id=1000"; NSURL *req ...
- hive报lzo Premature EOF from inputStream错误
今天dw组同事发邮件说有一个问题让帮解决一下.他们自己没能搞得定.下面问题解决过程: 1.hql insert overwrite table mds_prod_silent_atten_user p ...
- System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x800AC472
更新至服务器后运行出错: System.Runtime.InteropServices.COMException: Exception from HRESULT: 0x800AC472 解决方法 注册 ...
- MassTransit_契约的创建
消息契约的命名规范 命令模式:动词+对象 事件模式:对象+动词 不同模式下要求不同的主键 Creating a message contract In MassTransit, a message c ...
- Android客户端与PHP服务端交互(一)---框架概述
背景 作为一个普通上班族,总是想做一些自认为有意义的事情,于是乎准备成立一个工作室,尽管目前正在筹备阶段,但是之前有些朋友提出一些需求的时候,我发现自己的能力还是有限,直到最近和一些技术牛朋友聊起这事 ...
- [转载]CSS教程:实例讲解定位Position
http://www.missyuan.com/thread-395406-1-1.html 1. position:static 所有元素的默认定位都是:position:static,这意味着元素 ...
- WinStore开发知识导航集锦
1.页面导航与页面传值:http://blog.csdn.net/tcjiaan/article/details/7895487
- C# 使用 SAP NCO3.0 调用SAP RFC函数接口
最近使用C#调用SAP RFC函数,SAP提供了NCO3.0组件. 下载组件安装,之后引用“sapnco.dll”和“sapnco_utils.dll”两个文件. 在程序中 using SAP.Mid ...
- thinkphp 3.2.3+Admin LTE后台框架