CyclicBarrier

接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier。CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到所有线程都达到了这个屏障时,再一起继续执行后面的动作。看一下CyclicBarrier的使用实例:

public static class CyclicBarrierThread extends Thread
{
private CyclicBarrier cb;
private int sleepSecond; public CyclicBarrierThread(CyclicBarrier cb, int sleepSecond)
{
this.cb = cb;
this.sleepSecond = sleepSecond;
} public void run()
{
try
{
System.out.println(this.getName() + "运行了");
Thread.sleep(sleepSecond * 1000);
System.out.println(this.getName() + "准备等待了, 时间为" + System.currentTimeMillis());
cb.await();
System.out.println(this.getName() + "结束等待了, 时间为" + System.currentTimeMillis());
}
catch (Exception e)
{
e.printStackTrace();
}
}
} public static void main(String[] args)
{
Runnable runnable = new Runnable()
{
public void run()
{
System.out.println("CyclicBarrier的所有线程await()结束了,我运行了, 时间为" + System.currentTimeMillis());
}
};
CyclicBarrier cb = new CyclicBarrier(3, runnable);
CyclicBarrierThread cbt0 = new CyclicBarrierThread(cb, 3);
CyclicBarrierThread cbt1 = new CyclicBarrierThread(cb, 6);
CyclicBarrierThread cbt2 = new CyclicBarrierThread(cb, 9);
cbt0.start();
cbt1.start();
cbt2.start();
}

看一下运行结果:

Thread-0运行了
Thread-2运行了
Thread-1运行了
Thread-0准备等待了, 时间为1444650316313
Thread-1准备等待了, 时间为1444650319313
Thread-2准备等待了, 时间为1444650322313
CyclicBarrier的所有线程await()结束了,我运行了, 时间为1444650322313
Thread-2结束等待了, 时间为1444650322313
Thread-0结束等待了, 时间为1444650322313
Thread-1结束等待了, 时间为1444650322313

从运行结果看,由于是同一个CyclicBarrier,Thread-0先运行到了await()的地方,等着;Thread-2接着运行到了await()的地方,还等着;Thread-1最后运行到了await()的地方,所有的线程都运行到了await()的地方,所以三个线程以及指定的Runnable"同时"运行后面的代码,可以看到,await()之后,四个线程运行的时间一模一样,都是1444650322313。

从使用来看,可能有人觉得CyclicBarrier和CountDownLatch有点像,都是多个线程等待相互完成之后,再执行后面的代码。实际上,CountDownLatch和CyclicBarrier都是用于多个线程间的协调的,它们二者的几个差别是:

1、CountDownLatch是在多个线程都进行了latch.countDown()后才会触发事件,唤醒await()在latch上的线程,而执行countDown()的线程,执行完countDown()后会继续自己线程的工作;CyclicBarrier是一个栅栏,用于同步所有调用await()方法的线程,线程执行了await()方法之后并不会执行之后的代码,而只有当执行await()方法的线程数等于指定的parties之后,这些执行了await()方法的线程才会同时运行

2、CountDownLatch不能循环使用,计数器减为0就减为0了,不能被重置;CyclicBarrier提供了reset()方法,支持循环使用

3、CountDownLatch当调用countDown()方法的线程数等于指定的数量之后,可以唤起多条线程的任务;CyclicBarrier当执行await()方法的线程等于指定的数量之后,只能唤起一个BarrierAction

注意,因为使用CyclicBarrier的线程都会阻塞在await方法上,所以在线程池中使用CyclicBarrier时要特别小心,如果线程池的线程过少,那么就会发生死锁了

Callable、Future和FutureTask

Callable

Callable和Runnable差不多,两者都是为那些其实例可能被另一个线程执行的类而设计的,最主要的差别在于Runnable不会返回线程运算结果,Callable可以(假如线程需要返回运行结果)

Future

Future是一个接口表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。Future提供了get()、cancel()、isCancel()、isDone()四种方法,表示Future有三种功能:

1、判断任务是否完成

2、中断任务

3、获取任务执行结果

FutureTask

FutureTask是Future的实现类,它提供了对Future的基本实现。可使用FutureTask包装Callable或Runnable对象,因为FutureTask实现了Runnable,所以也可以将FutureTask提交给Executor。

使用方法

Callable、Future、FutureTask一般都是和线程池配合使用的,因为线程池ThreadPoolExecutor的父类AbstractExecutorService提供了三种submit方法:

1、public Future<?> subit(Runnable task){...}

2、public <T> Future<T> submit<Runnable task, T result>{...}

3、public <T> Future<T> submit<Callable<T> task>{...}

第2个用得不多,第1个和第3个比较有用

Callable+Future使用示例

public static class CallableThread implements Callable<String>
{
public String call() throws Exception
{
System.out.println("进入CallableThread的call()方法, 开始睡觉, 睡觉时间为" + System.currentTimeMillis());
Thread.sleep(10000);
return "123";
}
} public static void main(String[] args) throws Exception
{
ExecutorService es = Executors.newCachedThreadPool();
CallableThread ct = new CallableThread();
Future<String> f = es.submit(ct);
es.shutdown(); Thread.sleep(5000);
System.out.println("主线程等待5秒, 当前时间为" + System.currentTimeMillis()); String str = f.get();
System.out.println("Future已拿到数据, str = " + str + ", 当前时间为" + System.currentTimeMillis());
}

运行结果为:

进入CallableThread的call()方法, 开始睡觉, 睡觉时间为1444654421368
主线程等待5秒, 当前时间为1444654426369
Future已拿到数据, str = 123, 当前时间为1444654431369

看到任意一个利用Callable接口submit上去的任务,只要有一个Future接受它,Future便可以在程序任何地点尝试去获取这条线程返回出去的数据,时间可以比对一下,正好10000ms,即10s

Callable+FutureTask使用示例

有兴趣的可以看下源码,其实使用Callable+Future的方式,es.submit(ct)方法返回的Future,底层实现new出来的是一个FutureTask。那么,我们看一下Callable+FutureTask的方式:

public static class CallableThread implements Callable<String>
{
public String call() throws Exception
{
System.out.println("进入CallableThread的call()方法, 开始睡觉, 睡觉时间为" + System.currentTimeMillis());
Thread.sleep(10000);
return "123";
}
} public static void main(String[] args) throws Exception
{
ExecutorService es = Executors.newCachedThreadPool();
CallableThread ct = new CallableThread();
FutureTask<String> f = new FutureTask<String>(ct);
es.submit(f);
es.shutdown(); Thread.sleep(5000);
System.out.println("主线程等待5秒, 当前时间为" + System.currentTimeMillis()); String str = f.get();
System.out.println("Future已拿到数据, str = " + str + ", 当前时间为" + System.currentTimeMillis());
}

看下运行结果:

进入CallableThread的call()方法, 开始睡觉, 睡觉时间为1444655049199
主线程等待5秒, 当前时间为1444655054200
Future已拿到数据, str = 123, 当前时间为1444655059200

和上面的写法运行结果一样,就不解释了

使用Callable、Future和FutureTask的好处

上面演示了两个例子,其实反映的是现实中一种情况,把上面的例子稍微扩展一下就是:

有一个method()方法,方法中执行方法A返回一个数据要10秒钟,A方法后面的代码一共要执行20秒钟,但是这20秒的代码中有10秒的方法并不依赖方法A的执行结果,有10秒钟的代码依赖方法A的执行结果。此时若采用同步的方式,那么势必要先等待10秒钟,等待方法A执行完毕,返回数据,再执行后面20秒的代码。

不得不说这是一种低效率的做法。有了Callable、Future和FutureTask,那么:

1、先把A方法的内容放到Callable实现类的call()方法中

2、method()方法中,Callable实现类传入Executor的submit方法中

3、执行后面方法中10秒不依赖方法A运行结果的代码

4、获取方法A的运行结果,执行后面方法中10秒依赖方法A运行结果的代码

这样代码执行效率一下子就提高了,程序不必卡在A方法处。

当然,也可以不用Callable,采用实现Runnable的方式,run()方法执行完了想个办法给method()方法中的某个变量V赋个值就好了。但是我上一篇文章开头就说了,之所以要用多线程组件,就是因为JDK帮我们很好地实现好了代码细节,让开发者更多可以关注业务层的逻辑。如果使用Runnable的方式,那么我们自己就要考虑很多细节,比如Runnable实现类的run()方法执行完毕给V赋值是否线程安全、10秒后如果A方法没有执行完导致V还没有值怎么办,何况JDK还给用户提供了取消任务、判断任务是否存在等方法。既然JDK已经帮我们考虑并实现这些细节了,在没有有说服力的理由的情况下,我们为什么还要自己写run()方法的实现呢?

Java多线程21:多线程下的其他组件之CyclicBarrier、Callable、Future和FutureTask的更多相关文章

  1. Java并发编程:ThreadPoolExecutor + Callable + Future(FutureTask) 探知线程的执行状况

    如题 (总结要点) 使用ThreadPoolExecutor来创建线程,使用Callable + Future 来执行并探知线程执行情况: V get (long timeout, TimeUnit ...

  2. java 22 - 21 多线程之多线程的代码实现方式3

    JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法 A.public static ExecutorService newCachedThreadPool() B.public s ...

  3. java 多线程 33: 多线程组件之 Callable、Future和FutureTask

    Callable Callable和rRunnable差不多,两者都是为那些其实例可能被另一个线程执行的类而设计的,最主要的差别在于Runnable不会返回线程运算结果,Callable可以(假如线程 ...

  4. 多线程-Callable、Future、FutureTask

    我们普遍知道的创建线程的方式有两种,一种是继承Thread,一种是实现Runnable接口.这两种方式都无法获取任务执行完后的结果,并发包提供了Callable 类能够得到任务执行完的结果. 为何需要 ...

  5. java中Future与FutureTask使用与分析

    Future与FutureTask都是用于获取线程执行的返回结果.下面我们就对两者之间的关系与使用进行一个大致的介绍与分析 一.Future与FutureTask介绍: Future位于java.ut ...

  6. java 多线程 30: 多线程组件之 CyclicBarrier

    CyclicBarrier 接着讲多线程下的其他组件,第一个要讲的就是CyclicBarrier.CyclicBarrier从字面理解是指循环屏障,它可以协同多个线程,让多个线程在这个屏障前等待,直到 ...

  7. 【Java EE 学习 22 下】【单线程下载】【单线程断点下载】【多线程下载】

    一.文件下载简述 1.使用浏览器从网页上下载文件,Servlet需要增加一些响应头信息 (1)response.setContentType("application/force-downl ...

  8. java并发与多线程面试题与问题集合

    http://www.importnew.com/12773.html     https://blog.csdn.net/u011163372/article/details/73995897    ...

  9. [转]Java中的多线程你只要看这一篇就够了

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

随机推荐

  1. 用muduo实现memcached协议的例子

    最近花了两天时间用 muduo 部分实现了 memcached 服务器协议,代码位于 examples/memcached/server,能通过 memcached 的大部分测试用例(incr/dec ...

  2. 海量数据相似度计算之simhash和海明距离

    通过 采集系统 我们采集了大量文本数据,但是文本中有很多重复数据影响我们对于结果的分析.分析前我们需要对这些数据去除重复,如何选择和设计文本的去重算法?常见的有余弦夹角算法.欧式距离.Jaccard相 ...

  3. linux开机启动程序

    一./etc/rc.local这是一个最简单的方法,编辑“/etc/rc.local”,把启动程序的shell命令输入进去即可(要输入命令的全路径),类似于windows下的“启动”. 使用命令 vi ...

  4. 122. Best Time to Buy and Sell Stock(二) leetcode解题笔记

    122. Best Time to Buy and Sell Stock II Say you have an array for which the ith element is the price ...

  5. 一个login

    login 1.获取提交表单,保存到变量中.2.判断用户密码是否正确,利用Model类.3.验证用户是否激活.3.判断用户是否记住登录状态,是的话,将其用cookie和session分别保存.没有的话 ...

  6. android接收短信——framework处理流程(android 5.1)

    modem层不懂,所以直接从RIL.java开始.以电信卡接收短信为例 modem通知RIL.java中的 RILReceiver处理接收信息 class RILReceiver implements ...

  7. 转:Xms Xmx PermSize MaxPermSize 区别

    Eclipse崩溃,错误提示:MyEclipse has detected that less than 5% of the 64MB of Perm Gen (Non-heap memory) sp ...

  8. 慕课网Java高并发秒杀学习

    课程地址:http://www.imooc.com/learn/587 一个很好:spring,springMVC,mybatis,bootstrap,jQuery,mysql,Restful学习案例 ...

  9. CSS3使用AnimationEnd为同一个元素添加多个动画效果

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name ...

  10. 『TCP/IP详解——卷一:协议』读书笔记——10

    2013-08-22 22:57:17 3.8 ifconfig命令 这个命令在Linux系统下可以通过下面的指令阅读说明文档: ifconfig 由于书中作者用的系统比较早的某Unix系统,所以我的 ...