转自  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的更多相关文章

  1. thread_fork/join并发框架1

    一.线程并发控制  Thread.Executor.ForkJoin和Actor  1.裸线程      Runnable接口,调用.start()开始,没有现成的API来结束线程,你需要自己来实现, ...

  2. Java 7 Fork/Join 并行计算框架概览

    应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在 很多的应用程序在运行在多核 ...

  3. Java 并发系列之十:java 并发框架(2个)

    1. Fork/Join框架 2. Executor框架 3. ThreadPoolExecutor 4. ScheduledThreadPoolExecutor 5. FutureTask 6. t ...

  4. 深入理解Java并发框架AQS系列(一):线程

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...

  5. 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)

    一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...

  6. 协程并发框架gevent及其用法

    gevent是python的一个并发框架,采用协程实现并发目的,用起来也非常简单 gevent的docs:http://www.gevent.org/contents.html 一个最简单的例子: i ...

  7. Python 开源异步并发框架的未来

    http://segmentfault.com/a/1190000000471602 开源 Python 是开源的,介绍的这几个框架 Twisted.Tornado.Gevent 和 tulip 也都 ...

  8. Python开源异步并发框架

    Python开源异步并发框架的未来 2014年3月30日,由全球最大的中文IT社区CSDN主办的“开源技术大会·” (Open Source Technology Conference ,简称OSTC ...

  9. 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 ...

随机推荐

  1. Atlas+Keepalived系列二:管理Atlas

    1:登录代理端口1234 [root@localhost bin]# mysql -uroot -p -P1234 -h127.0.0.1 proxy-address项配置,例如proxy-addre ...

  2. Android SDK镜像的介绍使用

    由于一些原因,Google相关很多服务都无法访问,所以在很多时候我们SDK也无法升级,当然通过技术手段肯定可以解决,但是比较麻烦,而且下载速度也不怎么样. 这里笔者介绍一个国内的Android镜像站, ...

  3. Android开发(二十五)——Android上传文件至七牛

    设置头像: Drawable drawable = new BitmapDrawable(dBitmap); //Drawable drawable = Drawable.createFromPath ...

  4. java项目中读取properties文件

    这里的配置文件都放在src下面, System.properties的内容 exceptionMapping=exceptionMapping.properties config=config.pro ...

  5. adding validation annotators to model classes 在linq to EntityFrame的Model中添加前台验证validation annotators

    The same solution can be applied for LINQ to SQL. The snippet the article shows for using the Metada ...

  6. Why restTemplate.put() throws “HttpClientErrorException: 404 Not Found”

      I make a put request RestTemplate restTemplate = new RestTemplate(); restTemplate.put(new URI(&quo ...

  7. C#记录对象的变化

    经常,我们会遇到一个场景,在保存对象到数据库之前,对比内存对象和数据库值的差异. 下面我写了一种实现,为保存定义一个事件,然后自动找出对象之间的差异,请注意,并没有通过反射的方式去获取每个属性及其值. ...

  8. Reflector反编译.NET文件后修复【转】

    反编译后的工程文件用VS2010打开后,在打开窗体时会出现一系列错误提示: 第一种情况: “设计器无法处理第 152 行的代码: base.AutoScaleMode = AutoScaleMode. ...

  9. 记一次VNC远程连接Linux问题解决记录(5900端口测试、KDE桌面安装)

    最近几天,到一个项目上安装Linux部署环境.由于服务器在机房,而进机房又比较麻烦,于是选择VNC远程连接Linux就显得自然而然了.以前也用过VNC,而且还经常使用,由于各个项目环境不太一样,这次也 ...

  10. C# 调用 WebService 连接ORACLE 11g

    这几天开发一个WebService遇到很多问题,记录下来顺便帮助一下以后遇到情况的人. 我是通过ADO.NET来连接ORACLE的,也可以用ORACLE提供的ODP.NET. 通过正常的连接后部署II ...