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. linux 安装vscode

    滚动安装vscode 需要先添加源,然后install 以centos为例: sudo rpm --import https://packages.microsoft.com/keys/microso ...

  2. 20145310 《Java程序设计》第5周学习总结

    20145310 <Java程序设计>第5周学习总结 教材学习内容总结 本周主要进行第八章和第九章的学习. java中所有的错误都会打包为对象,可以try catch代表错误的对象后做一些 ...

  3. JavaScript的this指针到底指向哪?

    编程过程中,着实十分困扰this的指向性,经过查阅一番资料,终于搞清楚了,在这里总结一下,全文分为以下三个部分: 什么是this指针? this指针指向哪里? 何时使用this? 一 什么是this指 ...

  4. OGNL与ValueStack(VS)-N语法top语法(转)

    N语法[0]:<s:property value="[0]"/><br> N语法[1]:<s:property value="[1]&quo ...

  5. javascript的函数,定义

    函数 function       A               ()          { }函数体开头    函数名    传入的参数     方法 <button onclick=&qu ...

  6. jsp选项卡导航实现——模板

    效果 刚进来页面的样子 在第二个选项卡上方时 点击后 离开 同样第三个 点击 移走鼠标 代码 <%@ page contentType="text/html;charset=UTF-8 ...

  7. django教程目录

    什么是web框架? Do a web framework ourselves MVC和MTV模式 django的流程和命令行工具 Django的配置文件(settings) Django URL (路 ...

  8. Memcached get 命令

    Memcached get 命令获取存储在 key(键) 中的 value(数据值) ,如果 key 不存在,则返回空. 语法: get 命令的基本语法格式如下: get key 多个 key 使用空 ...

  9. [Vue]vue中各选项及钩子函数执行顺序

    在vue中,实例选项和钩子函数和{{}}表达式都是不需要手动调用就可以直接执行的. 一.生命周期图示 二.vue中各选项及钩子函数执行顺序 1.在页面首次加载执行顺序有如下: beforeCreate ...

  10. spring mvc:内部资源视图解析器2(注解实现)@Controller/@RequestMapping

    spring mvc:内部资源视图解析器2(注解实现)  @Controller/@RequestMapping 访问地址: http://localhost:8080/guga2/hello/goo ...