文章目录

  • 1:JUC是什么
  • 2:倒计数器——CountDownLatchDemo
  • 3:循环栅栏——CyclicBarrier
  • 4:允许多个线程同时访问——Semaphore(信号量)

1:JUC是什么

JUC全名java.util.concurrent是一种在并发编程中使用的工具类
JUC主要是三大包构成:并发包,并发原子包,并发lock包

如下介绍的三种辅助类都是在java.util.concurrent包下

2:倒计数器——CountDownLatchDemo

CountDownLatch是一个非常实用的多线程控制工具类。"Count Down”在英文中意为倒计数,Latch 意为门闩的意思,这里简单地称之为倒计数器。在这里,门闩的含义是把门锁起来,不让里面的线程跑出来。因此,这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计数结束,再开始执行。
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行

下面这个简单的实例,演示CountDownLatch用法
题目:班长(主线程)监督六个同学上自习,假设第一个同学八点就走了,第二个九点就走了。。要求六个同学走完了,班长(主线程)才可以走
代码演示

public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {3      CountDownLatch countDownLatch=new CountDownLatch(6);for (int i=0;i<6;i++){new Thread(()->{System.out.println(Thread.currentThread().getName()+"离开教室");7               countDownLatch.countDown();},String.valueOf(i)).start();}10       countDownLatch.await();System.out.println(Thread.currentThread().getName()+"班长关门走人");}}

上述代码第3行生成一个CountDownLatch 实例,计数数量为6, 这表示需要6个线程完成任务后等待在CountDownLatch上的线程(主线程)才 能继续执行。代码第7行使用了CountDownL atch.countdown()方法,也就是通知CountDownLatch, 一个线程已经完成了任务倒计数器减1。第10行使用CountDownLatch.awai()方法,要求主线程等待所有检查任务全部完成,待6个任务全部完成后,主线程才能继续执行。

主线程在CountDownLatch等待,当所有任务全部完成以后,主线程才可以继续执行

如上例子类似的一个 典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要对各项设备、仪器进行检查。只有等所有检查都完成后,引擎才能点火。这种场景就非常适合使用CountDownLatch。它可以使点火线程等待所有检查线程全部完工后再执行。

3:循环栅栏——CyclicBarrier

CyclicBarrier是另外一种多线程并发控制工具。和CountDownLatch非常类似,它也可以实现线程间的计数等待,但它的功能比CountDownLatch更加复杂且强大。

CylicBarrier可以理解为循环栅栏,栅栏就是一种障碍物, 比如,通常在别墅的周围就可以围上一圈栅栏,阻止闲杂人等入内。(用来阻止线程继续执行,要求线程在栅栏外等待)前面Cyelic意为循环,也就是说这个计数器可以反复使用。比如,我们将计数器设置为10,那么凑齐第一批10个线程后,计数器就会归零,接着凑齐下一批10个线程,这就是循环栅栏内在的含义。
CyclicBarrier 比CountDownLatch略微强大些, 它可以接收一个参数作为barrierAction.所谓barrierAction就是当计数器一次计数完成后, 系统会执行的动作。如下构造函数,其中,parties表示计数总数,也就是参与的线程总数。

CyclicBarrier(int parties, Runnable barrierAction)
//创建一个新的CyclicBarrier,当给定数量的参与方(线程)正在等待它时,
//它将触发,当触发barrier时,它将执行给定的barrier操作,
//该操作由进入barrier的最后一个线程执行。

CyclicBarrier的使用场景也很丰富。比如,召唤七龙珠,只有集齐了七颗龙珠才可以召唤神龙。
下面的代码示例使用CyclicBarrier演示召唤七龙珠,召唤神龙的场景

public class CyclicBarrierDemo {public static void main(String[] args) {3       CyclicBarrier cyclicBarrier= new CyclicBarrier(7,()-> System.out.println("**召唤神龙"));for (int i=0;i<7;i++){final int tempt=i;
6            new Thread(()->{System.out.println(Thread.currentThread().getName()+"收集到第:"+tempt+"颗龙珠");try {9                    cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}}
}
3收集到第:3颗龙珠
0收集到第:0颗龙珠
1收集到第:1颗龙珠
2收集到第:2颗龙珠
4收集到第:4颗龙珠
5收集到第:5颗龙珠
6收集到第:6颗龙珠
**召唤神龙

上述代码第3行创建了CyliBrrier 实例,并将计数器设置为7,要求在计数器达到指标时,执行第3行的barrierAction。每一个收集龙珠的线程都会执行第6行定义的run()方法。在第9行,每一个收集龙珠的线程都会等待,直到所有的收集龙珠的线程都集合完毕。集合完毕意味着CyelicBarrier 的一次计 数完成.。一旦任务全部完成,第3行定义的barrierAction就会被调用,打印相关信息。

在第9行后再加一段代码

10 dowork();//  private static void dowork() {  System.out.println("龙珠准备好了"); }
11 cyclicBarrier.await();

执行效果

//篇幅有限
4收集到第:4颗龙珠
**召唤神龙
龙珠准备好了
//篇幅有限
**召唤神龙

当再次调用cyclicBarrier.await()方法时, 会进行下次计数。第10行模拟了龙珠准备好了的任务。当一个龙珠准备好了,他就会要求CyelicBarrier开始下一次计数,这次计数主要目的是监控是否所有的龙珠都准备好了。一旦所有的龙珠都准备好了,第3行定义的barrierAction就会被调用,打印相关信息。

CyelicBarrier.await()方法可能会抛出两个异常。一个是InteruptedException, 也就是在等待过程中,线程被中断,应该说这是一个非常通用的异常。 大部分迫使线程等待的方法都可能会抛出这个异常,使得线程在等待时依然可以响应外部紧急事件。另外一个异常则是CyelicBarrier 特有的BrokenBarrierException。一旦遇到这个异常,则表示当前的CyclicBarrier已经破损了,可能系统已经没有办法等待所有线程到齐了。如果继续等待,可能就是徒劳无功,赶快回家吧。

4:允许多个线程同时访问——Semaphore(信号量)

信号量为多线程协作提供了更为强大的控制方法, 广义上说,信号量是对锁的扩展。无论是内部锁synchronized,还是重入锁RentantLtock,一次都只允许一个线程访问一个资源。可信号量却可以指定多个线程,同时访问某一个资源。 信号量主要提供了以下构造函数:

public Semaphore(int permits)
public Semaphore(int pemits, boolean fair)//第二个参数可以指定是否公平

在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有:

public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire (long timeout, TimeUnit unit)
public void release ()

acquire()方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放个许可或者当前线程被中断。acquireUninterruptibly()方法和acquire0方法类似,但是不响应中断。tryAcquire()方法尝试获得一个许可, 如果成功则返回true, 失败则返回false, 它不会进行等待,只会立即返回。release()方法用于在线程访问资源结束后释放一个许可,以使其他等待许可的线程可以进行资源访问。

下面用一个简单的争车位例子来介绍Semaphore使用
争车位,以前都是多线程去抢一个资源,如果现在是多对多呢?比如现在六个人去停三个车位,就是六个人去争三个车位,怎么抢呢?言下之意就是信号量可以控制多线程的并发数
代码演示

public class SemaphoreDemo {public static void main(String[] args) {3        Semaphore semaphore = new Semaphore(3);//模拟资源类,3个停车位for (int i=0;i<6;i++){new Thread(()->{6              try {semaphore.acquire();System.out.println(Thread.currentThread().getName()+"获取到了停车位");Thread.sleep(3000);//在停车位停3秒离开System.out.println(Thread.currentThread().getName()+"离开了停车位");} catch (InterruptedException e) {e.printStackTrace();}finally {14                    semaphore.release();15               }},String.valueOf(i)).start();}}
}
1获取到了停车位
0获取到了停车位
3获取到了停车位
0离开了停车位
1离开了停车位
3离开了停车位
4获取到了停车位
5获取到了停车位
2获取到了停车位
4离开了停车位
5离开了停车位
2离开了停车位

第3行代码声明了一个包含3个许可的信号量。这就意味着同时可以有3个线程进入代码段第6-15行。第6-15行为临界区管理代码,程序会限制执行这段代码的线程数。申请信号量使用acquire()方法操作,在离开时,务必使用release()方法释放信号量(代码第14行)。这就和释放锁是一个道理。如果不幸发生了信号量的泄露(申请了但没有释放),那么可以进入临界区的线程数量就会越来越少,直到所有的线程均不可访问。在本例中,同时开启6个线程。观察这段程序的输出,你就会发现系统以3个线程一组为单位, 依次输出获取到了停车位,离开了停车位。

如上代码如果注释掉了第14行,出现如下结果。说明发生了信号量的泄露(申请了但没有释放),那么可以进入临界区的线程数量就会越来越少,并且程序运行不会停止,线程一直等待

1获取到了停车位
2获取到了停车位
0获取到了停车位
2离开了停车位
1离开了停车位
0离开了停车位

如上代码如过把第3行的 Semaphore semaphore = new Semaphore(3);换成
Semaphore semaphore = new Semaphore(1);就等价于synchronized。
多线程抢占资源,如果我们要求抢到的资源停留30秒中,我们就可以使用信号量

JUC——JUC强大辅助类讲解相关推荐

  1. Day127.JUC:线程间通信(Conditon)、并发容器类(CopyOnWrite)、JUC强大辅助类、Callable

    . 目录 一.线程间通信 线程间通信改造成Lock版  Condition 定制化调用通信 Condition 二.并发容器类 (解决集合安全问题) CopyOnWrite 写时拷贝技术 三.JUC ...

  2. JUC基础(一): 什么是JUC(JUC概述)

    什么是JUC JUC简介 在Java中,线程部分是一个重点,本篇文章说的JUC也是关于线程的. JUC就是java.util.concurrent工具包的简称. 这是一个处理线程的工具包, JDK1. ...

  3. JUC阻塞队列BlockingQueue讲解

    概述 阻塞队列有两个特性: 阻塞:当队列为空时,会阻塞队列弹出操作,直到队列不为空.当队列满了时,会阻塞入队操作,直到队列不满. 队列:FIFO,先进先出. 接口:java.util.concurre ...

  4. Java并发包JUC的Lock锁讲解

    概述 Java有两种锁,一种是使用关键字Synchronized对方法或者代码块进行加锁,一种是使用接口Lock(实际上其实现类)进行上锁和解锁. 区别: Synchronized是java的一个关键 ...

  5. JUC介绍--常用辅助类(CountDownLatch CyclicBarrier Semaphore)

    CountDownLatch减法计数器 每次有线程调用countDownLatch.countDown()数量就-1 数量减到0时,countDownLatch.await()会被唤醒,继续向下执行 ...

  6. 【Java】高并发-JUC:JUC中的Condition对象

    1.Condition使用简介 从整体上来看Object的wait和notify/notify是与对象监视器配合完成线程间的等待/通知机制,而Condition与Lock配合完成等待通知机制,前者是j ...

  7. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  8. JUC高并发编程从入门到精通(全)

    目录 前言 1. Lock接口 1.1 复习synchronized 1.2 Lock接口 1.3 Lock方法 1.4 两者差异 2. 线程间通信 2.1 synchronized案例 2.2 Lo ...

  9. JUC笔记之尚硅谷周阳老师思维导图整理

    文章目录 1. JUC 是什么 2. Lock 接口 3. Lambda表达式复习--详情请看 on java 8 4. 线程间通信 5. 线程间定制化调用通信 6.NotSafeDemo 7.多线程 ...

最新文章

  1. 机器学习_周志华_问题汇总_第2周
  2. 实现不可变类如何禁止子类化?
  3. 二、Windows下TortoiseGit的安装与配置
  4. mysql可以打开dbt么_dbt 基本试用
  5. Redis Linux 安装运行实战全记录
  6. harbor pull 失败
  7. nao机器人行走速度_震撼!寒冬腊月里惊现多台历途外墙清洗机器人
  8. 为什么python打不开_python文件打不开如何解决
  9. c调用python第三方库_Python使用ctypes模块调用DLL函数之C语言数组与numpy数组传递...
  10. 任达华遇袭是效仿“宏颜获水”事件?百度回应:严惩肇事者 以儆效尤
  11. Atitit 获取剪贴板内容
  12. 随手写了个android应用
  13. Robotium 常用方法
  14. 被中国家长摧残的十种优秀儿童品质(转)
  15. Mac无法开机?别着急看这里
  16. java项目开发实践经验每日总结(2014/2/22)
  17. 手机百度浏览器ua标识在哪里_浏览器标识(ua)的那些事
  18. 时序预测 | MATLAB实现贝叶斯优化CNN-LSTM时间序列预测(股票价格预测)
  19. MySQL各数据类型总结
  20. 高职高专院校的消防工作和措施

热门文章

  1. 影响项目进度的因素有哪些?如何跟踪项目计划?
  2. 动作游戏Demo(一)换装系统
  3. 数字摘要和数字签名等概念
  4. 2021年高压电工报名考试及高压电工考试试卷
  5. 单片机c语言篮球比分_基于单片机的篮球记分器设计报告
  6. jstat和jmap打印堆栈排查内存泄漏
  7. failed to allocate 2.00G (2147483648 bytes) from device: CUDA_ERROR_OUT_OF_MEMORY: out of memory
  8. 模拟部署FTP服务器并提供文件的上传及下载
  9. 自动驾驶—自动泊车之AVM环视系统算法框架
  10. 如何10分钟建立一个网站