锁的机制从整体的运行转态来讲核心就是:阻塞,解除阻塞,但是如果仅仅是这点功能,那么JUC并不能称为一个优秀的线程开发框架,然而是因为在juc里面提供了大量方便的同步工具辅助类。

Semaphore信号量

Semaphore通常用于限制可以访问某些资源(物理or逻辑)的线程数目。

例如,大家排队去银行办理业务,但是只有两个银行窗口提供服务,来了10个人需要办理业务,所以这10个排队的人员需要依次使用这两个业务窗口来办理业务。

观察Semaphore类的基本定义:

  1. public class Semaphore extends Object implements Serializable

Semaphore类中定义的方法有如下几个:

  • 构造方法:

    1. public Semaphore(int premits),设置服务的信号量;
  • 构造方法:
    1. public Semaphore(int premitsboolean fair) ,是否为公平锁;
  • 等待执行:
    1. public void acquireUninterruptibly(int permits)
    • 设置的信号量上如果有阻塞的线程对象存在,那么将一直持续阻塞状态。
  • 释放线程的阻塞状态:
    1. public void release(int permits);
  • 返回可用的资源个数:
    1. public int availablePermits();

范例:实现银行排队业务办理

  1. package so.strong.mall.concurrent;
  2. import java.util.Random;
  3. import java.util.concurrent.Semaphore;
  4. import java.util.concurrent.TimeUnit;
  5.  
  6. public class SemaphoreDemo {
  7. public static void main(String[] args) {
  8. final Semaphore semaphore = new Semaphore(2); //现在允许操作的资源一共有2个
  9. final Random random = new Random(); //模拟每一个用户办理业务的时间
  10. for (int i = 0; i < 10; i++) {
  11. new Thread(new Runnable() {
  12. @Override
  13. public void run() { //每一个线程就是一个要办理业务的人员
  14. if (semaphore.availablePermits() > 0) { //现有空余窗口
  15. System.out.println("[" + Thread.currentThread().getName() + "]进入银行,没有人办理业务");
  16. } else { //没有空余位置
  17. System.out.println("[" + Thread.currentThread().getName() + "]排队等候办理业务");
  18. }
  19. try {
  20. semaphore.acquire(); //从信号量中获得操作许可
  21. System.out.println("[" + Thread.currentThread().getName() + "]开始办理业务");
  22. TimeUnit.SECONDS.sleep(random.nextInt(10)); //模拟办公延迟
  23. System.out.println("[" + Thread.currentThread().getName() + "]结束业务办理");
  24. semaphore.release(); //当前线程离开办公窗口
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }, "顾客-" + i).start();
  30. }
  31. }
  32. }
  1. [顾客-0]进入银行,没有人办理业务
  2. [顾客-0]开始办理业务
  3. [顾客-1]进入银行,没有人办理业务
  4. [顾客-1]开始办理业务
  5. [顾客-2]排队等候办理业务
  6. [顾客-3]排队等候办理业务
  7. [顾客-4]排队等候办理业务
  8. [顾客-5]排队等候办理业务
  9. [顾客-6]排队等候办理业务
  10. [顾客-7]排队等候办理业务
  11. [顾客-8]排队等候办理业务
  12. [顾客-9]排队等候办理业务
  13. [顾客-0]结束业务办理
  14. [顾客-2]开始办理业务
  15. [顾客-1]结束业务办理
  16. [顾客-3]开始办理业务
  17. [顾客-2]结束业务办理
  18. [顾客-4]开始办理业务
  19. [顾客-3]结束业务办理
  20. [顾客-5]开始办理业务
  21. [顾客-4]结束业务办理
  22. [顾客-6]开始办理业务
  23. [顾客-5]结束业务办理
  24. [顾客-7]开始办理业务
  25. [顾客-7]结束业务办理
  26. [顾客-8]开始办理业务
  27. [顾客-6]结束业务办理
  28. [顾客-9]开始办理业务
  29. [顾客-8]结束业务办理

这种信号量的处理在实际开发中有什么用呢?例如,现在对于数据库的连接一共有2个连接,那么可能有10个用户等待进行数据库操作,能够使用的连接个数为2个,这样10个用户就需要排队依次使用这两个连接来进行数据库操作。

CountDownLatch闭锁

CoundDownLatch描述的是一个计数的减少,下面首先观察一个程序的简单问题:

范例:编写一个简单的多线程开发

  1. package so.strong.mall.concurrent;
  2. public class CountDownDemo {
  3. public static void main(String[] args) {
  4. for (int i = 0; i < 2; i++) {
  5. new Thread(new Runnable() {
  6. @Override
  7. public void run() {
  8. System.out.println("["+Thread.currentThread().getName()+"]线程应用执行完毕");
  9. }
  10. },"线程对象-"+i).start();
  11. }
  12. System.out.println("[***主线程***]所有的程序执行完毕");
  13. }
  14. }
  1. [***主线程***]所有的程序执行完毕
  2. [线程对象-1]线程应用执行完毕
  3. [线程对象-0]线程应用执行完毕  

现在可以发现,对于此时应该保证所有的线程执行完毕后在执行程序的输出计算,就好比:旅游团集合人员乘车离开。应该保证所有的线程都执行完毕了(指定个数的线程),这样的话就必须做一个计数处理。

CoundDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。

CoundDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

CoundDownLatch类之中的常用方法有如下几个:

  • 构造方法:

    1. public CountDownLatch(int count); //要设置一个等待的线程个数;
  • 减少等待个数:

    1. public void countDown();  
  • 等待countDownLatch为0:

    1. public void await() throws InterruptedException;

      

范例:利用CountDownLatch解决之前的设计问题

  1. package so.strong.mall.concurrent;
  2. import java.util.concurrent.CountDownLatch;
  3.  
  4. public class CountDownDemo {
  5. public static void main(String[] args) throws Exception {
  6. final CountDownLatch countDownLatch = new CountDownLatch(2); //2个线程全部执行完毕后可继续执行
  7. for (int i = 0; i < 2; i++) {
  8. new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. System.out.println("[" + Thread.currentThread().getName() + "]线程应用执行完毕");
  12. countDownLatch.countDown(); //减少等待的线程个数
  13. }
  14. }, "线程对象-" + i).start();
  15. }
  16. countDownLatch.await(); //等待计数的结束(个数为0)
  17. System.out.println("[***主线程***]所有的程序执行完毕");
  18. }
  19. }
  1. [线程对象-0]线程应用执行完毕
  2. [线程对象-1]线程应用执行完毕
  3. [***主线程***]所有的程序执行完毕

  

CyclicBarrier栅栏

CyclicBarrierCountDownLatch是非常类似的,CyclicBarrier核心的概念是在于设置一个等待线程的数量边界,到达了此边界之后进行执行。

CyclicBarrier类是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点(Common Barrier Point)。

CyclicBarrier类是一种同步机制,它能够对处理一些算法的线程实现同。换句话讲,它就是一个所有线程必须等待的一个栅栏,直到所有线程都到达这里,然后所有线程才可以继续做其他事情。

通过调用CyclicBarrier对象的await()方法,两个线程可以实现互相等待。一旦N个线程在等待CyclicBarrier达成,所有线程将被释放掉去继续执行。

CyclicBarrier类的主要方法如下:

  • 构造方法:

    1. public CyclicBarrier(int parties);//设置等待的边界;
  • 傻傻的等待其他线程:
    1. public int await() throws InterruptedException, BrokenBarrierException
  • 等待其他线程:
    1. public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException
  • 重置等待的线程个数:
    1. public void reset();

范例:观察CyclicBarrier进行等待处理

  1. package so.strong.mall.concurrent;
  2. import java.util.concurrent.CyclicBarrier;
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. public class CyclicBarrierDemo {
  6. public static void main(String[] args) throws Exception{
  7. final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
  8. for (int i = 0; i < 3 ; i++) {
  9. final int second = i;
  10. new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
  14. try {
  15. TimeUnit.SECONDS.sleep(second);
  16. cyclicBarrier.await(); //等待处理
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
  21. }
  22. },"娱乐者-"+i).start();
  23. }
  24. }
  25. }
  1. [娱乐者-0]-等待开始
  2. [娱乐者-1]-等待开始
  3. [娱乐者-2]-等待开始
  4. [娱乐者-1]-等待结束
  5. [娱乐者-0]-等待结束

  如果不想一直等待则可以设置超时时间,则超过了等待时间之后将会出现"TimeoutException"。

  1. package so.strong.mall.concurrent;
  2. import java.util.concurrent.CyclicBarrier;
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. public class CyclicBarrierDemo {
  6. public static void main(String[] args) throws Exception{
  7. final CyclicBarrier cyclicBarrier = new CyclicBarrier(2); //当凑够2个线程金进行触发
  8. for (int i = 0; i < 3 ; i++) {
  9. final int second = i;
  10. new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println("["+Thread.currentThread().getName()+"]-等待开始");
  14. try {
  15. TimeUnit.SECONDS.sleep(second);
  16. cyclicBarrier.await(6,TimeUnit.SECONDS); //等待处理
  17. } catch (Exception e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("["+Thread.currentThread().getName()+"]-等待结束");
  21. }
  22. },"娱乐者-"+i).start();
  23. }
  24. }
  25. }
  1. [娱乐者-0]-等待开始
  2. [娱乐者-1]-等待开始
  3. [娱乐者-2]-等待开始
  4. [娱乐者-1]-等待结束
  5. [娱乐者-0]-等待结束
  6. Disconnected from the target VM, address: '127.0.0.1:63717', transport: 'socket'
  7. [娱乐者-2]-等待结束
  8. java.util.concurrent.TimeoutException
  9. at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:250)
  10. at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:427)
  11. at so.strong.mall.concurrent.CyclicBarrierDemo$1.run(CyclicBarrierDemo.java:21)
  12. at java.lang.Thread.run(Thread.java:745)

CyclicBarrier还有一个特点是可以进行重置处理

范例:重置处理

  1. package so.strong.mall.concurrent;
  2. import java.util.concurrent.CyclicBarrier;
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. public class CyclicBarrierResetDemo {
  6. public static void main(String[] args) throws Exception {
  7. final CyclicBarrier cb = new CyclicBarrier(2); //当凑够2个线程就进行触发
  8. for (int i = 0; i < 3; i++) {
  9. final int second = i;
  10. new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println("[" + Thread.currentThread().getName() + "]-等待开始");
  14. try {
  15. if (second == 2) {
  16. cb.reset(); //重置
  17. System.out.println("[重置处理****]" + Thread.currentThread().getName());
  18. } else {
  19. TimeUnit.SECONDS.sleep(second);
  20. cb.await(6,TimeUnit.SECONDS);//等待处理
  21. }
  22. } catch (Exception e) {
  23. e.printStackTrace();
  24. }
  25. System.out.println("[" + Thread.currentThread().getName() + "]-等待结束");
  26. }
  27. }, "娱乐者-" + i).start();
  28. }
  29. }
  30. }
  1. [娱乐者-0]-等待开始
  2. [娱乐者-1]-等待开始
  3. [娱乐者-2]-等待开始
  4. [重置处理****]娱乐者-2
  5. [娱乐者-2]-等待结束
  6. [娱乐者-1]-等待结束
  7. [娱乐者-0]-等待结束

CountDownLatch与CyclicBarrier的区别

  • CountDownLatch最大的特征是进行一个数据减法的操作等待,所有的统计操作一旦开始之中就必须执行countDown()方法,如果等待个数不是0,就被一只等待,并且无法重置。
  • CyclicBarrier设置一个等待的临界点,并且可以有多个等待线程出现,只要满足了临界点就触发了线程的执行代码后将重新开始进行计数处理操作,也可以直接利用reset()方法执行重置操作。

JUC——线程同步辅助工具类(Semaphore,CountDownLatch,CyclicBarrier)的更多相关文章

  1. JUC——线程同步辅助工具类(Exchanger,CompletableFuture)

    Exchanger交换空间 如果现在有两个线程,一个线程负责生产数据,另外一个线程负责消费数据,那么这个两个线程之间一定会存在一个公共的区域,那么这个区域的实现在JUC包之中称为Exchanger. ...

  2. 并发工具类:CountDownLatch、CyclicBarrier、Semaphore

    在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch ...

  3. 30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验?

    30行自己写并发工具类(Semaphore, CyclicBarrier, CountDownLatch)是什么体验? 前言 在本篇文章当中首先给大家介绍三个工具Semaphore, CyclicBa ...

  4. 【同步工具类】CountDownLatch闭锁任务同步

    [同步工具类]CountDownLatch闭锁任务同步 转载:https://www.cnblogs.com/yangchongxing/p/9214284.html 打过dota的同学都知道,多人一 ...

  5. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  6. Java线程的并发工具类

    Java线程的并发工具类. 一.fork/join 1. Fork-Join原理 在必要的情况下,将一个大任务,拆分(fork)成若干个小任务,然后再将一个个小任务的结果进行汇总(join). 适用场 ...

  7. 【Java并发工具类】CountDownLatch和CyclicBarrier

    前言 下面介绍协调让多线程步调一致的两个工具类:CountDownLatch和CyclicBarrier. CountDownLatch和CyclicBarrier的用途介绍 CountDownLat ...

  8. 类型转换辅助工具类TypeCaseHelper

    package org.sakaiproject.util; import java.math.BigDecimal; import java.sql.Date; import java.sql.Ti ...

  9. 多线程状态与优先级、线程同步与Monitor类、死锁

    一.线程状态 二.线程优先级 三.初步尝试多线程 class Program { static void Main(string[] args) { while (true) { MessagePri ...

随机推荐

  1. Shell定时删除日志

    vim del_log.sh #!/bin/bash location="/home/dl/code/logs" find $location -mtime +4 -type f ...

  2. JS和css实现检测移动设备方向的变化并判断横竖屏幕

    这篇文章主要介绍了JS和css实现检测移动设备方向的变化并判断横竖屏幕,本文分别给出实现代码,需要的朋友可以参考下 方法一:用触发手机的横屏和竖屏之间的切换的事件  [自测可用, chrome , 手 ...

  3. linux用户相关及/etc/passed,/etc/group,/etc/shadow

    useradd:新建用户 usermod:修改用户相关信息 userdel:删除用户分(-r选项) 组的操作与用户的操作类似 选项 userdel相关选项: -f:强制删除用户,即使用户已登录 -r: ...

  4. 【[SDOI2017]数字表格】

    求 \[Ans=\prod_{i=1}^N\prod_{j=1}^MFib[(i,j)]\] 连乘的反演,其实并没有什么不一样 我们把套路柿子拿出来 \[F(n)=\sum_{i=1}^N\sum_{ ...

  5. MySQL 更改数据库数据存储目录

    MySQL数据库默认的数据库文件位于 /var/lib/mysql 下,有时候由于存储规划等原因,需要更改 MySQL 数据库的数据存储目录. 下文总结整理了实践过程的操作步骤.   1 确认MySQ ...

  6. 一维maxpooling

    index存储的是下标 vector<int> maxpooling(vector<int> num,int size){ vector<int> result; ...

  7. Redis——总结

    启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis 127.0.0.1:6379> redis 127.0 ...

  8. oracle之修改/忘记用户密码

    一.修改/忘记用户密码: ## 修改oracle用户名和密码 sqlplus /nolog ## DBA角色进入 conn /as sysdba; ## 查看用户列表 select username ...

  9. new的三种形态

    C++语言一直被认为是复杂编程语言中的杰出代表之一,不仅仅是因为其繁缛的语法规则,还因为其晦涩的术语.下面要讲的就是你的老熟人—new: 它是一个内存管理的操作符,能够从堆中划分一块区域,自动调用构造 ...

  10. ZOJ3202-Second-price Auction(根据输入输出判断是队列还是栈)

    A Stack or A Queue? Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu S ...