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. spring项目gitignore

    target/ ### STS ### .apt_generated .classpath .factorypath .project .settings .springBeans ### Intel ...

  2. ELK之elasticsearch5.6的安装和head插件的安装

    这里选择的elasticsearch为5.6的新版本,根据官方文档有几种暗装方式: https://www.elastic.co/guide/en/elasticsearch/reference/cu ...

  3. Spring笔记1——Spring起源及其核心技术

    Spring的作用 当我们使用一种技术时,需要思考为什么要使用这门技术.而我们为什么要使用Spring呢?从表面上面SSH这三大框架中,Struts是负责MVC责任的分离,并且提供为Web层提供诸如控 ...

  4. JavaConfig 使用Java代码进行显示配置

    从Spring 3起,JavaConfig功能已经包含在Spring核心模块,它允许开发者将bean定义和在Spring配置XML文件到Java类中. 需要先加载spring-context 包 &l ...

  5. vue2.0--vue-router路由

    一.vue-router如何进行参数传递 1.name  $route.name 通过在路由router/index.js中配置路由时定义的name属性来传递 ① 有一个页面components/he ...

  6. 面向对象-PHP面向对象的特性

     1.类和公有化 class Computer {     //什么叫做类内,就是创建类的花括号内的范围叫做类内,其他地方则类外.     //public 是对字段的公有化,这个字段类外即可访问,赋 ...

  7. (转)浅谈SQL Server 对于内存的管理

    简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...

  8. Hive 表结构操作

    添加列  add columns alter table table_name add columns (id int comment '主键ID' ) ; 默认在表所有字段之后,分区字段之前. 替换 ...

  9. .net core mvc部署到IIS导出Word 提示80070005拒绝访问

    项目中相信大家经常会遇到导出Word.Excel等需求,在实际开发环境中,一般不会出现什么问题,但当发布到IIS上后可能会遇到各种各样的问题,一般都是权限的问题.前几天把公司项目发布后,出现Word导 ...

  10. python异常列表

    http://www.runoob.com/python/python-exceptions.html https://www.cnblogs.com/zhangyingai/p/7097920.ht ...