java多线程应用场景不少,有时自己编写代码又不太容易实现,好在concurrent包提供了不少实现类,还有google的guava包更是提供了一些最佳实践,这让我们在面对一些多线程的场景时,有了不少的选择。

这里主要是看几个涉及到多线程等待的工具类。

一 CountDownLatch 一个或多个线程等待其他线程达到某一个目标后,再进行自己的下一步工作。而被等待的“其他线程”达到这个目标后,也继续自己下面的任务

看起来虽然比较绕,但是还算能理解。看几个场景:
跑步比赛,裁判需要等到所有的运动员(“其他线程”)都跑到终点(达到目标),才能去算排名和颁奖。
模拟并发,我需要启动100个线程去同时访问某一个地址,我希望它们能同时并发,而不是一个一个的去执行。

用法很简单,只有一个构造方法用于指明计数数量,然后就是await用于线程等待,countDown用于将计数器减1.
public CountDownLatch(int count) {  };  //参数count为计数值
public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { };  //将count值减1

工作原理就是让“其他线程”在合适的地方进行await等待,直到所有线程达到了某一个目标,然后再一起释放。

看代码:
赛跑的代码
import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * Created by wuwf on 17/7/17.
 * 一个线程或多个线程等待其他线程运行达到某一目标后进行自己的下一步工作,而被等待的“其他线程”达到这个目标后继续自己下面的任务。
 * <p/>
 *
 */
public class TestCountDownLatch {
    private CountDownLatch countDownLatch = new CountDownLatch(4);

    public static void main(String[] args) {
        TestCountDownLatch testCountDownLatch = new TestCountDownLatch();
        testCountDownLatch.begin();
    }

    /**
     * 运动员
     */
    private class Runner implements Runnable {
        private int result;
        public Runner(int result) {
            this.result = result;
        }

        @Override
        public void run() {
            try {
                //模拟跑了多少秒,1-3之间随机一个数
                Thread.sleep(result * 1000);

                System.out.println("运动员" + Thread.currentThread().getId() + "跑了" + result + "秒");

                //跑完了就计数器减1
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void begin() {
        System.out.println("赛跑开始");

        Random random = new Random(System.currentTimeMillis());
        for (int i = 0; i < 4; i++) {
            //随机设置每个运动员跑多少秒结束
            int result = random.nextInt(3) + 1;
            new Thread(new Runner(result)).start();
        }

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有人都跑完了,裁判开始算成绩");
    }
}

这段代码就是一个主线程,等待其他多个子线程完成某个任务后,再去执行下面的逻辑。

模拟并发的代码

import java.util.concurrent.CountDownLatch;

/**
 * Created by wuwf on 17/7/18.
 * 模拟N个线程同时启动
 */
public class TestManyThread {
    private CountDownLatch countDownLatch = new CountDownLatch(200);

    public static void main(String[] args) {
        new TestManyThread().begin();
    }

    public void begin() {
        for (int i = 0; i < 200; i++) {
            new Thread(new UserThread()).start();
            countDownLatch.countDown();
        }

        try {
            Thread.sleep(2000);
            System.out.println("线程并发");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private class UserThread implements Runnable {

        @Override
        public void run() {
            try {
                //等待所有线程
                countDownLatch.await();

                //TODO 在这里做客户端请求,譬如访问数据库之类的操作

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

需要注意,上面两个例子是不同的方式,一个是主线程等待其他线程达到某个目标后,自己再去完成一件事;第二个例子是多个线程达到某个目标后,继续完成各自的后续任务。
其中由第二个例子引申出下一个并发工具类。


二 CyclicBarrier 实现让一组线程等待至某个状态之后再全部同时执行,而且当所有等待线程被释放后,CyclicBarrier可以被重复使用。

这个也很好理解,大家约定好一起到XX地址汇合,所有人都到了以后再一起去吃饭。
它有两个构造方法
public CyclicBarrier(int parties, Runnable barrierAction) {
}

public CyclicBarrier(int parties) {
}

parties代表让多少个线程等待,Runnable属性是一个新线程,代表所有线程达到状态、等待完毕后,会执行的任务。
同样的也有两个await方法

public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

第一个代表线程挂起开始等待,一直等到达到目标状态,第二个代表等待一段指定的时间后,如果还没释放,就直接继续执行,不await了。

看代码
import java.util.concurrent.CyclicBarrier;

/**
 * Created by wuwf on 17/7/18.
 */
public class TestCyclicBarrier {
    private CyclicBarrier cyclicBarrier = new CyclicBarrier(5);

    public static void main(String[] args) {
        new TestCyclicBarrier().begin();
    }

    public void begin() {
        for (int i = 0; i < 5; i++) {
            new Thread(new Student()).start();
        }

    }

    private class Student implements Runnable {

        @Override
        public void run() {
            try {
                System.out.println("学生" + Thread.currentThread().getId() + "正在赶往XX饭店的路上");
                Thread.sleep(2000);
                //到了就等着,等其他人都到了,就进饭店
                cyclicBarrier.await();
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println("大家都到了,进去吃饭吧!");
        }
    }
}


可以看到这个工具类的目的,主要是为了限制一组线程在达到某个条件后,强制进行等待,直到最后一个线程也执行完await之前的逻辑后,再所有线程一起走await后面的。
与CountDownLatch不同,它不需要去自己维护那个CountDown每次减1的操作,与之相反,CyclicBarrier是每次都加1,直到加到构造方法里设定的值。
所以使用这个类一样可以完成上面CountDownLatch的第二个例子,就是模拟N个线程并发,而且用法比CountDownLatch更简单一些。

要说起这两个类的区别,我觉得更多的初衷是CountDownLatch目的是让一个线程等待其他N个线程达到某个条件后,自己再去做某个事(通过CyclicBarrier的第二个构造方法,在新线程里做事可以达到同样的效果)。而CyclicBarrier的目的是让N多线程互相等待直到所有的都达到某个状态,然后这N个线程再继续执行各自后续(通过CountDownLatch在某些场合也能完成类似的效果)。
根据场景选择更简单的那个就好。



Java并发编程之CountDownLatch,CyclicBarrier实现一组线程相互等待、唤醒的更多相关文章

  1. Java 并发编程中的 CyclicBarrier 用于一组线程互相等待

    Java 5 引入的 Concurrent 并发库软件包中的 CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point) ...

  2. Java并发编程之CountDownLatch

    一.场景描述 在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景 例如:百米赛跑,十名运动员同时起跑,由于速度的快慢,肯定有先到达和后到达的,而终点有个统计成绩的仪器,当所有选手到达终点时 ...

  3. Java并发编程之CountDownLatch的用法

    一.含义 CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能.CountDownLatch是一个同步的辅助类,它可以允许一个或多个线程等待, ...

  4. Java并发编程之CyclicBarrier

    一.场景描述 有四个游戏玩家玩游戏,游戏有三个关卡,每个关卡必须要所有玩家都到达后才能允许通过.其实这个场景里的玩家中如果有玩家A先到了关卡1,他必须等到其他所有玩家都到达关卡1时才能通过,也就是说线 ...

  5. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  6. Java并发编程之CAS第一篇-什么是CAS

    Java并发编程之CAS第一篇-什么是CAS 通过前面几篇的学习,我们对并发编程两个高频知识点了解了其中的一个—volatitl.从这一篇文章开始,我们将要学习另一个知识点—CAS.本篇是<凯哥 ...

  7. Java并发编程之CAS二源码追根溯源

    Java并发编程之CAS二源码追根溯源 在上一篇文章中,我们知道了什么是CAS以及CAS的执行流程,在本篇文章中,我们将跟着源码一步一步的查看CAS最底层实现原理. 本篇是<凯哥(凯哥Java: ...

  8. Java并发编程之CAS第三篇-CAS的缺点及解决办法

    Java并发编程之CAS第三篇-CAS的缺点 通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理.那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论 本篇是<凯 ...

  9. Java并发编程之set集合的线程安全类你知道吗

    Java并发编程之-set集合的线程安全类 Java中set集合怎么保证线程安全,这种方式你知道吗? 在Java中set集合是 本篇是<凯哥(凯哥Java:kagejava)并发编程学习> ...

随机推荐

  1. 20145303《Java程序设计》实验三实验报告

    20145303<Java程序设计>实验三实验报告 ssh公钥配置及git安装: eclipse中git配置: 队友链接: http://www.cnblogs.com/5321z/p/5 ...

  2. spring cron表达式及解析过程

    1.cron表达式 cron表达式是用来配置spring定时任务执行时间的字符串,由5个空格分隔成的6个域构成,格式如下: {秒}  {分}  {时}  {日}  {月}  {周} 每一个域的含义解释 ...

  3. Python-自省机制

     help 如果说能够通过一个函数就能够学会 Python,那这个函数一定就是 Python 提供的第一 个自带说明 help().help 函数的作用就是查看对象的帮组文档.比如: >> ...

  4. com.mysql.jdbc.MysqlDataTruncation:Data Truncation:Data too long for column '字段name' at row 1

    1.问题描述: 在mysql插入数据的时候报错:Cause: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long fo ...

  5. 从0开始 Java实习 黑白棋

    黑白棋的设计 代码如下: import java.util.*; public class Chess{ char[][] chess = new char[16][16]; public stati ...

  6. .net知识点汇总

    死锁的必要条件?怎么克服? 答:系统的资源不足,进程的推进的顺序不合适,资源分配不当,一个资源每次只能被一个进程使用,一个资源请求资源时,而此时这个资源已阻塞,对已获得资源不放,进程获得资源时,未使用 ...

  7. 关于 Token,你应该知道的十件事

    转自:http://ju.outofmemory.cn/entry/134189 原文是一篇很好的讲述 Token 在 Web 应用中使用的文章,而这是我和 Special 合作翻译的译文. 1. T ...

  8. 淘汰算法 LRU、LFU和FIFO

    含义: FIFO:First In First Out,先进先出LRU:Least Recently Used,最近最少使用 LFU:Least Frequently Used,最不经常使用 以上三者 ...

  9. Hive 导入、导出数据

    <------ 导入 ------> 从本地目录文件导入Hive表 语法:LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABL ...

  10. oracle 11g安装过程中问题:移动bin\oralbac11.dll 到bin\oralbac11.dll.dbl出错

    解决方法: 直接找到oralbac11.dll.dbl这个文件,将其删除即可.   http://blog.sina.com.cn/s/blog_51beaf0e0101000v.html