一、Fork/Join

Java7提供了Fork/Join用于并行执行任务的框架, 可以把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。

如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决,对开发来说也不再需要处理各种并行相关事务,例如同步、通信、死锁等问题,需要做的就是拆分任务并组合每个子任务的中间结果。

1.工作窃取

JDK1.7引入的Fork/Join框架就是基于工作窃取算法,是指某个线程从其他队列里窃取任务来执行。工作窃取算法的优点是充分利用线程进行并行计算,并减少了线程间的竞争,其缺点是在某些情况下还是存在竞争,比如双端队列里只有一个任务时。并且消耗了更多的系统资源,比如创建多个线程和多个双端队列。

一般会使用双端队列,比如AB线程分别处理AB两个任务队列,当有一个线程执行完一个任务队列时,会去窃取另一个未完成的队列任务,而被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。

2.类关系和API

RecursiveAction不需要返回值。

RecursiveTask通过泛型参数设置计算的返回值类型。

ForkJoinPool提供了一系列的submit方法,计算任务。ForkJoinPool默认的线程数通Runtime.availableProcessors()获得,因为在计算密集型的任务中,获得多于处理性核心数的线程并不能获得更多性能提升。

在RecursiveTask类中最重要的是实现compute()接口,此处定义了计算和拆分方法。

fork() - 每个子任务在调用fork方法时,又会进入compute方法,如果还要拆分则继续递归调用。

join() - 使用join方法会等待子任务执行完并得到其结果。

invokeAll() - 可变参数,触发所有输入的Task来计算。

假设在计算一个求和算法时,就可以采用Fork/Join方法进行并行计算。

public class Calculator extends RecursiveTask {

private static final int THRESHOLD = 100;

private int start;

private int end;

public Calculator(int start, int end) {

this.start = start;

this.end = end;

}

@Override

protected Integer compute() {

int sum = 0;

if((start - end) < THRESHOLD){

for(int i = start; i< end;i++){

sum += i;

}

}else{

int middle = (start + end) /2;

Calculator left = new Calculator(start, middle);

Calculator right = new Calculator(middle + 1, end);

left.fork();

right.fork();

sum = left.join() + right.join();

}

return sum;

}

}

二、CountDownLatch ( 重点! )

CountDownLatch,又名发令枪。这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

1.API及用法

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量,每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

设定初始值,即构造函数

//参数count为计数值

public CountDownLatch(int count) { };

主线程调用await等待

//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行

public void await() throws InterruptedException { };

//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行

public boolean await(long timeout, TimeUnit unit) throws InterruptedException{};

任务线程执行完计数

//将count值减1

public void countDown() { };

2.使用场景

实现最大的并行性:有时想同时启动多个线程,实现最大程度的并行性。例如测试一个单例类,如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。

开始执行前等待n个线程完成各自任务(应用最广):例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了。

死锁检测:一个非常方便的使用场景是,你可以使用n个线程访问共享资源,在每次测试阶段的线程数目是不同的,并尝试产生死锁。

三、CyclicBarrier

CyclicBarrier——回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行,叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。

//参数parties指让多少个线程或者任务等待至barrier状态

public CyclicBarrier(int parties) {}

//参数barrierAction为当这些线程都达到barrier状态时会执行的内容

public CyclicBarrier(int parties, Runnable barrierAction) {}

CyclicBarrier中最重要的方法就是await方法,有重载方法但是一般采用:

//用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务

public int await() throws InterruptedException, BrokenBarrierException { };

处理同一个动作有多个线程同时执行的场景,用一个栅栏卡在同一个状态,只有当所有线程抵达该状态了,才可以全部放行。

public class TestCyclicBarrier {

public static void main(String[] args) {

//1.得到一个CyclicBarrier实例

CyclicBarrier cb = new CyclicBarrier(4);

new Thread(new Fishing(cb),"1").start();

new Thread(new Fishing(cb),"2").start();

new Thread(new Fishing(cb),"3").start();

new Thread(new Fishing(cb),"4").start();

}

static class Fishing implements Runnable{

CyclicBarrier cb;

public Fishing(CyclicBarrier cb) {

this.cb = cb;

}

@Override

public void run() {

try {

cb.await();

System.out.println("第(" + Thread.currentThread().getName()

+ ")个人开始钓鱼");

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

}

}

}

CountDownLatch和CyclicBarrier的区别:

CountDownLatch强调的是一个线程(或多个)需要等待另外的n个线程干完某件事情之后才能继续执行。

CyclicBarrier强调的是n个线程,大家相互等待,只要有一个没完成,所有人都得等着。

CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

四、Semaphore(推荐)

Semaphore——信号量,控制同时访问某个特定资源的线程数量,用在流量控制。

Semaphore可以控同时访问的线程个数,一个信号量有且仅有3种操作,且它们全部是原子的:初始化、增加和减少。其包含两个构造方法:

//参数permits表示许可数目,即同时可以允许多少线程进行访问

public Semaphore(int permits) {

sync = new NonfairSync(permits);

}

//这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可

public Semaphore(int permits, boolean fair) {

sync = (fair)? new FairSync(permits) : new NonfairSync(permits);

}

acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。

release()用来释放许可。注意,在释放许可之前,必须先获获得许可。

//获取一个许可

public void acquire() throws InterruptedException { }

//获取permits个许可

public void acquire(int permits) throws InterruptedException { }

//释放一个许可

public void release() { }

//释放permits个许可

public void release(int permits) { }

五、Callable、Future和FutureTask

java.lang.Runnable接口,在它里面只声明了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。如果想要返回结果,则可以采用Callable。

Callable接口位于java.util.concurrent包下,里面只声明了一个方法call():

public interface Callable {

V call() throws Exception;

}

若需要执行Callable接口的方法,还需配合ExecutorService来调用submit()方法使之执行:

Future submit(Callable task);

Future> submit(Runnable task);

Future,对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

public interface Future {

boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit)

throws InterruptedException, ExecutionException, TimeoutException;

}

FutureTask,对Future接口的具体实现(也是唯一实现),既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

public FutureTask(Callable callable) { }

public FutureTask(Runnable runnable, V result) { }

实际使用的时候,可先定义Callble类、并重写call方法:

public class Task implements Callable{

@Override

public Integer call() throws Exception {

System.out.println("子线程在进行计算");

Thread.sleep(3000);

int sum = 0;

for(int i=0;i<100;i++)

sum += i;

return sum;

}

}

接着可以有两种方式去启动有返回值的线程方法:

结合ExecutorService开启多线程

//创建线程池

ExecutorService executor = Executors.newCachedThreadPool();

//创建Callable对象任务

Task task = new Task();

//提交任务并获取执行结果

Future result = executor.submit(task);

//关闭线程池,拿完线程就可以把池子关闭了,及时释放资源

executor.shutdown();

结合Thread开启多线程

//构造一个有返回值的并行任务

Task task = new Task();

//构造FutureTask

FutureTask futureTask = new FutureTask(task);

//构造一个新线程并注入FutureTask

Thread thread = new Thread(futureTask);

//开启线程

thread.start();

执行get()方法会阻塞到线程执行完得到结果

try {

if(futureTask.get()!=null){

System.out.println("task运行结果"+futureTask.get());

}else{

System.out.println("future.get()未获取到结果");

}

} catch (ExecutionException e) {

e.printStackTrace();

}

java 并发包_Java常用并发包相关推荐

  1. java 性能 排序_Java常用排序算法及性能测试集合

    package algorithm.sort; import java.lang.reflect.Method; import java.util.Arrays; import java.util.D ...

  2. java 类数组_Java常用类-字符串、日期类、算法及数组工具类等

    大家好,乐字节的小乐又和大家见面了.这次要给大家讲述的是Java常用类. 主要有以下知识点: Ø 字符串相关类(String .StringBuffer.StringBuilder) Ø 算法及数组工 ...

  3. java通用异常_Java常用异常整理

    填坑,整理下Java的常用异常.正确使用异常在实际编码中非常重要,但面试中的意义相对较小,因为对异常的理解和应用很难通过几句话或几行代码考查出来,不过我们至少应答出三点:异常类的继承关系.常用异常类. ...

  4. java英语词汇_java常用的英语单词

    java常用的英语单词 神秘的jave应该记住的英语单词有哪些呢?单词这么多,小编表示也很困扰.不过小编还是为大家整理出来了一些jave常用的英语单词,减轻大家负担. public['pʌblik] ...

  5. java所有单词_JAVA常用英语单词列表

    原标题:JAVA常用英语单词列表 第一章: public['pʌblik] 公共的,公用的 static['stætik] 静的;静态的;静止的 void:[vɔid] 空的 main:[mein] ...

  6. java 系统 类_Java常用实体类--System类

    字符串.日期.数字是Java程序中最常使用的数据对象,对这些数据的创建.修改.格式化和转换等操作融入在Java程序的每个角落,必须熟练掌握.本节将通过实例演示以下常用实体类Java系统级类:系统类Sy ...

  7. java md5库_Java常用类库API之MD5简单使用

    常用类库--MD5简单使用 MD5消息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash v ...

  8. JAVA外部库函数_java常用库函数

    库 Tools:工具 Preferences:参数配置 JSP 中常用英文 URL: Universal Resource Location:统一资源定位符 IE: Internet Explorer ...

  9. java api 框架_java常用对象API之集合框架

    说到集合框架,其实刚开始学的时候有点自我感觉很简单,自己认为就是集合类中的框架之类的,但是当自己简单把它过了一变后发现懵里懵懂的,什么都没懂,于是自己又认认真真的看了一遍,才弄明白. 说到集合框架就不 ...

  10. java calendar赋值_Java常用日期操作

    //日期转字符串,根据需要调整格式 @Testpublic voidDateToString(){ Date currentTime= newDate(); SimpleDateFormat form ...

最新文章

  1. linux c 启动程序吗,Linux下C程序启动时的系统调用
  2. ORACLE 10g 数据库体系结构图
  3. cacls 使用方法
  4. D - Connect the Cities (HDU - 3371)
  5. Java基础篇:final关键字
  6. CentOS7 安装 MySQL 和简单优化
  7. 金士顿 datatraveler写保护_【脑洞大开】金士顿推出限量版羽毛球闪存盘
  8. (9)vue.js 指令(1)
  9. oracle数据库inactive状态session过多的原因定位
  10. 北京时间的拼音及解释
  11. python 操作目录注意事项
  12. 魔咒词典 HDU - 1880 (字符串hash 单hash转int或者 双hash )
  13. 计算机专业毕业论文结尾,计算机毕业论文最后的总结.docx
  14. java中jsp是什么_JSP是什么?
  15. 推荐一款不错的嵌入式GUI(玲珑GUI)及在嵌入式linux上的移植
  16. 面对流氓软件,是逃避还是反击?(连载三、未来的流氓软件)
  17. 海底捞只要“面子”,不要“里子”?
  18. STM32CubeMX Crystal/Ceramic Resontor
  19. 实施微前端的六种方式
  20. 【转】Linux那些事儿之我是Hub(7)蝴蝶效应

热门文章

  1. 怎么寻找java实例,Java 实例 – 在指定目录中查找文件
  2. Mini-Batch 、Momentum、Adam算法的实现
  3. linux内核ip分片函数ip_fragment解析
  4. Windows开机运行程序
  5. python递归函数分叉树枝_python递归函数绘制分形树的方法
  6. 数据校验简介与C/C++代码实现
  7. 仓库无证如何处罚_法律问题 | 在船舶检查过程中,我遇到的一些问题该如何解决?...
  8. php rand js,js中的php rand函数
  9. docker 外部连接_Docker容器网络通信的那些事儿
  10. CentOS 6.7下 Samba服务器的搭建与配置(share共享模式)