ConCurrent in Practice小记 (4)#

Executors

Callable && Future <T>

Callable:此接口有一个call()方法。在这个方法中,必须实现任务的(处理)逻辑。Callable接口是一个参数化的接口。意味着必须指明call()方法返回的数据类型。

Future:也是一个接口,用来保证Callable对象结果的获取和管理。

Executors一般接受一个Callable对象,返回一个Future对象。可以取消任务,也可以查看任务的状态。使用get()方法得到call()的结果,在得到结果前会一直等待。如果get()被中断则会报InterruptionException,如果是在call()上面中断则报ExecutorsException。

Executors的invokeAny()方法,在多个任务同时执行时,得到第一个完成的任务的结果。输入是一个tasklist,仅仅给出正常完成的结果,如果所有的task线程都出现异常,则抛出异常。也有等待时限的版本。

同样的invokeAll()实现同样的功能,不过是针对所有的Future结果。

使用Future接口的cancel可以取消已经提交的任务,根据参数和任务状态的不同有以下几种可能:

  • 如果这个任务已经完成或之前的已被取消或由于其他原因不能被取消,那么这个方法将会返回false并且这个任务不会被取消。
  • 如果这个任务正在等待执行者获取执行它的线程,那么这个任务将被取消而且不会开始它的执行。如果这个任务已经正在运行,则视方法的参数情况而定。 cancel()方法接收一个Boolean值参数。如果参数为true并且任务正在运行,那么这个任务将被取消。如果参数为false并且任务正在运行,那么这个任务将不会被取消。

但是当使用Future的get方法来控制一个已经取消的任务,则抛出CancellationException异常。

也可以使用实现了Runnable的FutureTask类来得到线程计算的结果,其中get方法依然是在未得到结果前阻塞。call成功后会调用FutureTask中的done()方法。

CompletionService就是在内部维持了一个Future的队列,用来接受完成的任务,再此基础上通过poll和take取元素。这样就把处理结果和运行本身分开了,运行是自己开线程,而结果是在阻塞队列中的。

Fork/Join框架

Fork/Join框架主要是配合分治算法,同Executor框架不同的是,Fork/Join增加了work-steal算法,当执行任务的线程在等待join的时候,其工作线程可以去寻找其他未被执行的任务执行。

但是,由于该算法的特殊性,也使得Fork/Join框架有一定的局限性:

  • 任务只能使用fork()和join()操作,作为同步机制。如果使用其他同步机制,在同步期间,工作线程不能执行其他任务。比如,在Fork/Join框架中,使任务进入睡眠,在睡眠期间正在执行这个任务的工作线程将不会执行其他任务。
  • 任务不应该执行I/O操作,如读或写数据文件。
  • 任务不能抛出检查异常,它必须包括必要的代码来处理它们。

主要有两个类:

ForkJoinPool:实现了ExecutorService接口和work-stealing算法,管理工作线程等。

ForkJoinTask:执行任务的基类,提供在任务中执行fork()和join()的操作等等。通常情况下只要继承并实现该类的两个子类就可以,分别是没有返回值的RecursiveAction和有返回结果的RecursiveTask。

package com.lyb.Section5;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit; /**
* Created by lyb on 15-8-6.
*/
public class ForkJoinHelloWord {
public static void main(String[] args){
ProductListGenerator productListGenerator = new ProductListGenerator();
List<PenProduct> productList = productListGenerator.generate(10000);
PenTask task = new PenTask(productList,0,productList.size(),0.2);
ForkJoinPool pool = new ForkJoinPool();
pool.execute(task); do {
System.out.printf("Main: Thread count: %d \n",pool.getActiveThreadCount());
System.out.printf("Main: Thread steal: %d \n",pool.getStealCount());
System.out.printf("Main: Thread Parallelism: %d \n",pool.getParallelism());
try {
TimeUnit.MILLISECONDS.sleep(5);
}catch (InterruptedException e){
e.printStackTrace();
}
}while (!task.isDone()); pool.shutdown(); if (task.isCompletedNormally()){
System.out.printf("Main: The process has completed Normally!\n");
} for (int i = 0; i<productList.size(); i++){
PenProduct product = productList.get(i);
if (product.getPrice() != 12){
System.out.printf("PenPaoduct : %s , %d \n",product.getName(),product.getPrice());
}
}
System.out.printf("Main : End of the program. \n");
}
} class PenProduct{
private String name;
private double price; public double getPrice() {
return price;
} public void setName(String name) {
this.name = name;
} public void setPrice(double price) {
this.price = price;
} public String getName() {
return name;
}
} class ProductListGenerator{
public List<PenProduct> generate(int size){
List<PenProduct> ret = new ArrayList<>();
for (int i = 0; i<size; i++ ){
PenProduct product = new PenProduct();
product.setName("PenProduct" + i);
product.setPrice(10);
ret.add(product);
}
return ret;
} } class PenTask extends RecursiveAction{
private static final long serialVersionUID = 1L;
private List<PenProduct> penProducts;
private int first;
private int last;
private double increment; public PenTask(List<PenProduct> penTaskList, int first, int last, double increment){
this.penProducts = penTaskList;
this.first = first;
this.last = last;
this.increment = increment;
} @Override
protected void compute() {
if (last - first < 10){
updatePrice();
}else {
int middle = (first+last)/2;
System.out.printf("PenTask : Pending tasks: %s \n",getQueuedTaskCount());
PenTask task1 = new PenTask(penProducts,first,middle+1,increment);
PenTask task2 = new PenTask(penProducts,middle+1,last,increment);
invokeAll(task1, task2);
}
} private void updatePrice(){
for (int i = first; i < last; i++){
PenProduct product = penProducts.get(i);
product.setPrice(product.getPrice()*(1+increment));
}
}
}

使用ForkJoinPool的无参构造器会构造一个线程数等于计算机处理器数的池。由于这里不需要返回值,因此这里产生ForkJoinTask的对象继承自RecursiveAction类,当任务中处理的项目数大于10,就分解为两个项目,一次执行一个任务。

invokeAll()方法执行每个任务创建的子任务,需要完成的任务在它所有的子任务未完成之前都是等待,而工作线程也是。这和Executor框架有区别,在Executors中仅仅有任务被提交到池中,而在Fork/Join框架中还包括对任务的控制方法。

即使是使用一个ForkJoinPool类也可以提交Runnable或者是Callable进行执行。但是不会调用work-steal算法。

ForkJoinTask类还提供其他的方法来完成一个任务的执行,并返回一个结果,这就是complete()方法。这个方法接收一个RecursiveTask类的参数化类型的对象,并且当join()方法被调用时,将这个对象作为任务的结果返回。 它被推荐使用在:提供异步任务结果。

异步任务的提交

使用join(),fork()方法可以异步的将执行结果回传,每次fork(),都会把任务交给到池中,然后池中根据当前执行任务的线程数进行分配,并且将执行完毕的结果返回。最终在所有任务执行完成后join().

然后再使用get得到结果。

get()和join()有两个主要的区别:

join()方法不能被中断。如果你中断调用join()方法的线程,这个方法将抛出InterruptedException异常。

如果任务抛出任何未受检异常,get()方法将返回一个ExecutionException异常,而join()方法将返回一个RuntimeException异常。

抛出异常:

在ForkJoinTask类的compute方法中并不会抛出异常,但是必须有处理异常的代码,可以通过该类中其他的一些方法得到异常。

isCompletedAbnormally()会检测任务是否正常完成,而且可以设置:

Exception e=new Exception("This task throws an Exception: "+ "Task
from "+start+" to "+end);
completeExceptionally(e);

不抛出异常。

取消一个任务

ForkJoinTask也可以使用cancel(),但是需要注意的是:

  • ForkJoinPool类并没有提供任何方法来取消正在池中运行或等待的所有任务。
  • 当你取消一个任务时,你不能取消一个已经执行的任务。

所以,取消任务只是取消当前任务,并不影响其他线程的任务或者是缓存在池中的任务,但是取消任务的上下文切换的开销很大,基本上不值得,不如先返回在做一次执行。

ConCurrent in Practice小记 (4)的更多相关文章

  1. ConCurrent in Practice小记 (3)

    ConCurrent in Practice小记 (3) 高级同步技巧 Semaphore Semaphore信号量,据说是Dijkstra大神发明的.内部维护一个许可集(Permits Set),用 ...

  2. ConCurrent in Practice小记 (2)

    Java-ConCurrent2.html :first-child{margin-top:0!important}img.plugin{box-shadow:0 1px 3px rgba(0,0,0 ...

  3. ConCurrent in Practice小记 (1)

    ConCurrent in Practice小记 (1) 杂记,随书自己写的笔记: 综述问题 1.线程允许在同一个进程中的资源,包括共享内存,内存句柄,文件句柄.但是每个进程有自己的程序计数器,栈和局 ...

  4. Java中编写线程安全代码的原理(Java concurrent in practice的快速要点)

    Java concurrent in practice是一本好书,不过太繁冗.本文主要简述第一部分的内容. 多线程 优势 与单线程相比,可以利用多核的能力; 可以方便的建模成一个线程处理一种任务; 与 ...

  5. Concurrent.Thread.js

    (function(){ if ( !this.Data || (typeof this.Data != 'object' && typeof this.Data != 'functi ...

  6. 用happen-before规则重新审视DCL(转)

    编写Java多线程程序一直以来都是一件十分困难的事,多线程程序的bug很难测试,DCL(Double Check Lock)就是一个典型,因此对多线程安全的理论分析就显得十分重要,当然这决不是说对多线 ...

  7. 用happen-before规则重新审视DCL(转载)

    编写Java多线程程序一直以来都是一件十分困难的事,多线程程序的bug很难测试,DCL(Double Check Lock)就是一个典型,因此对多线程安全的理论分析就显得十分重要,当然这决不是说对多线 ...

  8. ThreadPoolExecutor最佳实践--如何选择线程数

    去年看过一篇<ThreadPoolExecutor详解>大致讲了ThreadPoolExecutor内部的代码实现. 总结一下,主要有以下四点: 当有任务提交的时候,会创建核心线程去执行任 ...

  9. 初识ThreadLocal

    近期公司在进行Java开发者的招聘活动,当中有一道面试题是这种:"请简单描写叙述一下ThreadLocal类的作用." 结果发现有非常多的面试者没有听说过ThreadLocal或者 ...

随机推荐

  1. java集合框架之比较器Comparator、Comparable

    参考http://how2j.cn/k/collection/collection-comparator-comparable/693.html Comparator 假设Hero有三个属性 name ...

  2. sql server 2008 R2 升级与安装遇到的问题

    因工作需要,遂把以前的2008升级到r2,升级失败,具体原因忘了,卸载2008,清了注册表删了文件,结果安装的时候失败了,如下图: 下一步-有错误日志和错误的序列号,错误日志在C:\Program F ...

  3. 20个Flutter实例视频教程-第05节: 酷炫的路由动画-1

    视屏地址: https://www.bilibili.com/video/av39709290/?p=5 博客地址: https://jspang.com/post/flutterDemo.html# ...

  4. HTML中&nbsp;&emsp等空格的区别

    HTML提供了5种空格实体(space entity),它们拥有不同的宽度,非断行空格( )是常规空格的宽度,可运行于所有主流浏览器.其他几种空格(       ‌‍)在不同浏览器中宽度各异.     ...

  5. YUV格式学习:YUV420P、YV12、NV12、NV21格式转换成RGB24(转载)

    转自:http://www.latelee.org/my-study/yuv-learning-yuv420p-to-rgb24.html 对于YUV420的格式,网上有一大堆资料,这里就不说了.直奔 ...

  6. Spring入门(四):使用Maven管理Spring项目

    让我们先回顾下本系列的前3篇博客: Spring入门(一):创建Spring项目 Spring入门(二):自动化装配bean Spring入门(三):通过JavaConfig装配bean 1.为什么要 ...

  7. E20181121-hm

    invoke vt. 乞灵,祈求; 提出或授引…以支持或证明; 召鬼; 借助

  8. 用spin和edit控件来用spin控制edit里面小数的增减

    1.响应SPIN的消息,就是点SPIN的上键头和下键头的消息,在这个消息里改变值是以0.1步进量增减.2.使用UpdateData(FALSE)来更新EDIT的关联的double型的变量. 创建步骤 ...

  9. Cocoapods在OS X Yosemite上报错的解决方法

    今天升级了Mac OS X 10.10-Yosemite以后运行pod install遇到下面的错误: /System/Library/Frameworks/Ruby.framework/Versio ...

  10. Java相关书籍阅读