1.概述

本文主要对Java中的关于并发的类的使用写一遍Demo.

具体涉及到的类有:

  1. CountdownLatch
  2. CyclicBarrier
  3. Semaphore

2.CountdownLatch

CountdownLatch类位于java.util.concurrent包下, 利用它可以实现类似计数器的功能, 比如有一个任务C, 它需要等到任务A和任务B执行完成之后才能执行. 此时就可以利用CountdownLatch进行实现.

2-1.构造方法

  1. public CountDownLatch(int count) {
  2. // 省略...
  3. }

count一般指线程的数量.

2-2.重要方法

  1. // 调用await()方法的线程会挂起, 它会等到count=0时才继续执行
  2. public void await() throws InterruptedException {
  3. // 省略...
  4. }
  5. // 与await()方法类似, 只不过是等待一段时间之后不管count值是多少, 都要继续向下执行
  6. public boolean await(long timeout, TimeUnit unit)
  7. throws InterruptedException {
  8. // 省略...
  9. }
  10. // 将count值减1
  11. public void countDown() {
  12. // 省略...
  13. }

2-3.使用示例

  1. import java.util.Random;
  2. import java.util.concurrent.CountDownLatch;
  3. public class CountdownLatchDemo {
  4. public static void main(String[] args) {
  5. new CountdownLatchDemo().start();
  6. }
  7. private void start() {
  8. // 定义线程的数量
  9. int num = 5;
  10. CountDownLatch latch = new CountDownLatch(num);
  11. for (int i = 0; i < num; i++) {
  12. new Thread(new RunImpl(latch), "线程"+i).start();
  13. }
  14. try {
  15. latch.await();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. System.out.println("所有线程写入完毕,继续处理其他任务...");
  20. }
  21. class RunImpl implements Runnable {
  22. private CountDownLatch latch;
  23. public RunImpl(CountDownLatch latch) {
  24. this.latch = latch;
  25. }
  26. @Override
  27. public void run() {
  28. try {
  29. System.out.println(Thread.currentThread().getName()+"正在写入数据...");
  30. Thread.sleep(new Random().nextInt(2000)); //以睡眠来模拟写入数据操作
  31. System.out.println(Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
  32. // 完成任务, 计数器减1
  33. latch.countDown();
  34. } catch (Exception e){
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. }

输出结果如下:

  1. 线程0正在写入数据...
  2. 线程1正在写入数据...
  3. 线程2正在写入数据...
  4. 线程3正在写入数据...
  5. 线程4正在写入数据...
  6. 线程1写入数据完毕,等待其他线程写入完毕
  7. 线程4写入数据完毕,等待其他线程写入完毕
  8. 线程3写入数据完毕,等待其他线程写入完毕
  9. 线程2写入数据完毕,等待其他线程写入完毕
  10. 线程0写入数据完毕,等待其他线程写入完毕
  11. 所有线程写入完毕,继续处理其他任务...

初始化时CountDownLatch的count为5, 随后启动了5个线程, 然后调用了latch.await(), 挂起main线程, 等待5个线程执行完毕. 每个线程执行完毕时调用了latch.countDown(); 让count减1, 等到count为0时main线程继续执行.

3.CyclicBarrier

与CountdownLatch类似, 只不过它可以进行重用, 而且还可以支持任务执行完之后随即选择一个线程来执行构造方法中传入的线程任务, 还有一点不同是, 主线程main不会堵塞.

3-1.构造方法

  1. public CyclicBarrier(int parties) {
  2. // 省略...
  3. }
  4. public CyclicBarrier(int parties, Runnable barrierAction) {
  5. // 省略...
  6. }

parties是指让多少个线程或者任务等待至barrier状态, 参数barrierAction是当所有线程都到达barrier状态时, 用最后一个到达barrier的线程执行barrierAction.

3-2.使用示例

  1. import java.util.Random;
  2. import java.util.concurrent.CyclicBarrier;
  3. public class CyclicBarrierDemo {
  4. public static void main(String[] args) {
  5. new CyclicBarrierDemo().start();
  6. }
  7. private void start() {
  8. int num = 5;
  9. CyclicBarrier barrier = new CyclicBarrier(num, () -> System.out.println("最后调用了线程: " + Thread.currentThread().getName()));
  10. for (int i = 0; i < num; i++) {
  11. new Thread(new RunImpl(barrier), "线程"+i).start();
  12. }
  13. // 等待上面线程执行完后再次执行
  14. // try {
  15. // Thread.sleep(10000);
  16. // } catch (InterruptedException e) {
  17. // e.printStackTrace();
  18. // }
  19. // for (int i = 0; i < num; i++) {
  20. // new Thread(new RunImpl(barrier), "线程"+i).start();
  21. // }
  22. System.out.println("所有线程写入完毕,继续处理其他任务...");
  23. }
  24. class RunImpl implements Runnable {
  25. private CyclicBarrier cyclicBarrier;
  26. public RunImpl(CyclicBarrier cyclicBarrier) {
  27. this.cyclicBarrier = cyclicBarrier;
  28. }
  29. @Override
  30. public void run() {
  31. try {
  32. System.out.println(Thread.currentThread().getName()+"正在写入数据...");
  33. Thread.sleep(new Random().nextInt(2000)); //以睡眠来模拟写入数据操作
  34. System.out.println(Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
  35. cyclicBarrier.await();
  36. System.out.println(Thread.currentThread().getName()+"await()完成.");
  37. } catch (Exception e){
  38. e.printStackTrace();
  39. }
  40. }
  41. }
  42. }

输出结果如下:

  1. 线程0正在写入数据...
  2. 线程2正在写入数据...
  3. 所有线程写入完毕,继续处理其他任务...
  4. 线程1正在写入数据...
  5. 线程3正在写入数据...
  6. 线程4正在写入数据...
  7. 线程1写入数据完毕,等待其他线程写入完毕
  8. 线程4写入数据完毕,等待其他线程写入完毕
  9. 线程2写入数据完毕,等待其他线程写入完毕
  10. 线程3写入数据完毕,等待其他线程写入完毕
  11. 线程0写入数据完毕,等待其他线程写入完毕
  12. 最后调用了线程: 线程0
  13. 线程0await()完成.
  14. 线程1await()完成.
  15. 线程2await()完成.
  16. 线程4await()完成.
  17. 线程3await()完成.

发现, main方法并不会等待所有线程执行完毕之后执行.

这里没有使用countDown()进行计数器减1, 然后使用await()方法等待计数器变为0, 而是使用await()方法, 并且在await()方法中进行计数器等于0的判断. 并且, 在所有线程到达barrier时, 用最后一个到达的线程去执行barrierAction.

4.Semaphore

Semaphore可以翻译为信号量, 它可以控制同时并发的线程数量, 通过acquire()方法获取许可, 如果没有就等待, 而release()释放一个许可, 注意: 在释放前必须先获取许可.

4-1.构造方法

  1. public Semaphore(int permits) {
  2. sync = new NonfairSync(permits);
  3. }
  4. public Semaphore(int permits, boolean fair) {
  5. sync = fair ? new FairSync(permits) : new NonfairSync(permits);
  6. }

permits是允许同时并发的线程数量, 而fair是控制是否允许等待时间越长的线程优先获取许可(公平与不公平).

4-2.重要方法

  1. public void acquire() throws InterruptedException {
  2. sync.acquireSharedInterruptibly(1);
  3. }
  4. public void release() {
  5. sync.releaseShared(1);
  6. }

acquire()方法获取许可, release()方法释放许可.

4-3.使用示例

  1. import java.util.Random;
  2. import java.util.concurrent.Semaphore;
  3. public class SemaphoreDemo {
  4. public static void main(String[] args) {
  5. new SemaphoreDemo().start();
  6. }
  7. private void start() {
  8. int num = 8; // 工人数
  9. Semaphore semaphore = new Semaphore(5); // 机器数目
  10. for (int i = 0; i < num; i++) {
  11. new Thread(new RunImpl(i, semaphore)).start();
  12. }
  13. }
  14. class RunImpl implements Runnable {
  15. private int num;
  16. private Semaphore semaphore;
  17. public RunImpl(int num, Semaphore semaphore){
  18. this.num = num;
  19. this.semaphore = semaphore;
  20. }
  21. @Override
  22. public void run() {
  23. try {
  24. semaphore.acquire();
  25. System.out.println("工人" + this.num + "占用一个机器在生产...");
  26. Thread.sleep(new Random().nextInt(2000)); // 以睡眠来模拟写入数据操作
  27. System.out.println("工人" + this.num + "释放出机器");
  28. semaphore.release();
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. }
  33. }
  34. }

输出结果如下:

  1. 工人0占用一个机器在生产...
  2. 工人3占用一个机器在生产...
  3. 工人2占用一个机器在生产...
  4. 工人1占用一个机器在生产...
  5. 工人4占用一个机器在生产...
  6. 工人4释放出机器
  7. 工人5占用一个机器在生产...
  8. 工人1释放出机器
  9. 工人6占用一个机器在生产...
  10. 工人6释放出机器
  11. 工人7占用一个机器在生产...
  12. 工人5释放出机器
  13. 工人0释放出机器
  14. 工人3释放出机器
  15. 工人2释放出机器
  16. 工人7释放出机器

这里控制并发线程数量为5个, 工人多了只能等待其它工人释放机器, 再去公平/不公平竞争去使用机器.

5.总结

  1. CountDownLatch一般用于线程A等待若干个线程执行完成任务之后, 它才执行. 不可以重用.
  2. CyclicBarrier一般用于一组线程相互等待至barrier时, 先用最后一个到达的线程执行barrierAction, 再同时执行await()之后的代码. 可以重用.
  3. Semaphore其实和锁有点类似, 用于控制对某个资源的并发控制.

Java并发辅助类的使用的更多相关文章

  1. Java并发编程的4个同步辅助类

    Java并发编程的4个同步辅助类(CountDownLatch.CyclicBarrier.Semphore.Phaser) @https://www.cnblogs.com/lizhangyong/ ...

  2. Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semaphore、Phaser)

    我在<JDK1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...

  3. Java并发编程的4个同步辅助类(CountDownLatch、CyclicBarrier、Semphore、Phaser)

    我在<jdk1.5引入的concurrent包>中,曾经介绍过CountDownLatch.CyclicBarrier两个类,还给出了CountDownLatch的演示案例.这里再系统总结 ...

  4. Java并发机制(8)--concurrent包下辅助类的使用

    Java并发编程:concurrent包下辅助类的使用 整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920397.html 1.CountDown ...

  5. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  6. 学习笔记:java并发编程学习之初识Concurrent

    一.初识Concurrent 第一次看见concurrent的使用是在同事写的一个抽取系统代码里,当时这部分代码没有完成,有许多的问题,另一个同事接手了这部分代码的功能开发,由于他没有多线程开发的经验 ...

  7. 【Java并发编程实战】-----“J.U.C”:锁,lock

    在java中有两种方法实现锁机制,一种是在前一篇博客中([java7并发编程实战]-----线程同步机制:synchronized)介绍的synchronized,而另一种是比synchronized ...

  8. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  9. Java并发编程实现概览

    并发概览 >>同步 如何同步多个线程对共享资源的访问是多线程编程中最基本的问题之一.当多个线程并发访问共享数据时会出现数据处于计算中间状态或者不一致的问题,从而影响到程序的正确运行.我们通 ...

随机推荐

  1. 使用POI读取xlsx文件,包含对excel中自定义时间格式的处理

    package poi; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundExcepti ...

  2. C#程序优化的50种方案

    一.用属性代替可访问的字段 1..NET数据绑定只支持数据绑定,使用属性可以获得数据绑定的好处: 2.在属性的get和set访问器重可使用lock添加多线程的支持. 二.readonly(运行时常量) ...

  3. N阶乘尾部的0个数

    N阶乘尾部的0个数 描述 设计一个算法,计算出n阶乘中尾部零的个数 思路: 1.1 * 2 * 3 * ... * n --> 1 * 2 * 3 * (2 * 2) * 5 * (2 * 3) ...

  4. ES5与ES6中的继承

    ES5继承在ES5中没有类的概念,所以一般都是基于原型链继承,具体的继承方法有以下几种: 父类: function Father (name) { this.name = name || 'sam' ...

  5. Centos7 firewall开放3306端口

    目录 Centos7 firewall开放3306端口 1. 查看防火墙状态 2. 关闭防火墙firewall 3. 关闭防火墙firewall后开启 4. 开启端口 5. 重启防火墙 6. 常用命令 ...

  6. JavaScript 快速排序详解

    使用的是<JavaScript数据结构与算法>一书中的快速排序,并加上自己的理解. 经测试,此算法的速度比内置的 sort 更快!而阮一峰的那个快排更像是归并排序,虽然写法简单很多,但是性 ...

  7. node.js创建简单服务测试请求数据

    工具:安装node: 1,创建文件夹 server, 2 ,在server文件夹下分别创建server.js 和 package.json 文件 3,server.js 代码: var express ...

  8. java 中异常处理示例并捕获完整异常内容

    public class Test { public static void main(String[] args) { try { int a = 1; int b = 0; int c = a/b ...

  9. 使用maven-tomcat7-plugins时调试出现source not found解决

    直接看下面的步骤: 步骤1: 步骤2: 步骤3: 步骤4:

  10. glog学习(一):glog的编译及demo

    windows平台: 1.下载glog代码.下载地址:https://github.com/google/glog 2.使用cmake工具,获得对应的工程文件sln. 3.打开sln文件,生成对应的l ...