工具类
CountDownLactch
简述
允许一个或多个线程等待,直到在其他线程中执行的一组操作完成,同步辅助。 CountDownLatch 类用给定的 计数 初始化。await 方法阻塞,直到由于 countDown() 方法的调用 而导致当前计数达到零后所有等待线程被释放,并且任何后续的 await 调用立即返回。这是一个一次性的计数操作,因为计数无法重置。 如果需要重置计数的版本,请考虑使用 **CyclicBarrier **类。
构造函数
public CountDownLatch ( int count) {
if ( count < 0 ) throw new IllegalArgumentException ( "count < 0" ) ;
this . sync = new Sync ( count) ;
}
await方法
public void await ( ) throws InterruptedException {
sync. acquireSharedInterruptibly ( 1 ) ;
}
public final void acquireSharedInterruptibly ( int arg)
throws InterruptedException {
if ( Thread . interrupted ( ) )
throw new InterruptedException ( ) ;
if ( tryAcquireShared ( arg) < 0 )
doAcquireSharedInterruptibly ( arg) ;
}
private void doAcquireSharedInterruptibly ( int arg)
throws InterruptedException {
final Node node = addWaiter ( Node . SHARED) ;
boolean failed = true ;
try {
for ( ; ; ) {
final Node p = node. predecessor ( ) ;
if ( p == head) {
int r = tryAcquireShared ( arg) ;
if ( r >= 0 ) {
setHeadAndPropagate ( node, r) ;
p. next = null ;
failed = false ;
return ;
}
}
if ( shouldParkAfterFailedAcquire ( p, node) &&
parkAndCheckInterrupt ( ) )
throw new InterruptedException ( ) ;
}
} finally {
if ( failed)
cancelAcquire ( node) ;
}
}
案例
package JUC. api;
import JUC. myenum. CountryEnum;
import java. util. concurrent. CountDownLatch ;
public class CountDownLatchDemo {
private int count = 6 ;
public int getCount ( ) {
return this . count;
}
public static void main ( String [ ] args) throws Exception {
fight ( ) ;
}
public static void fight ( ) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch ( new CountDownLatchDemo ( ) . getCount ( ) ) ;
for ( int i = 1 ; i < 7 ; i++ ) {
new Thread ( ( ) -> {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t被秦所灭。。。" ) ;
countDownLatch. countDown ( ) ;
} , CountryEnum . foreachCountryEnum ( i) . getResultMessage ( ) ) . start ( ) ;
}
countDownLatch. await ( ) ;
System . out. println ( "秦朝建立" ) ;
}
}
package JUC. myenum;
public enum CountryEnum {
ONE ( 1 , "齐" ) ,
TWO ( 2 , "楚" ) ,
THREE ( 3 , "燕" ) ,
FOUR ( 4 , "韩" ) ,
FIVE ( 5 , "赵" ) ,
SIX ( 6 , "魏" ) ;
private Integer resultCode;
private String resultMessage;
CountryEnum ( Integer resultCode, String resultMessage) {
this . resultCode = resultCode;
this . resultMessage = resultMessage;
}
public Integer getResultCode ( ) {
return resultCode;
}
public String getResultMessage ( ) {
return resultMessage;
}
public static CountryEnum foreachCountryEnum ( int index) {
CountryEnum [ ] values = CountryEnum . values ( ) ;
for ( CountryEnum value:
values) {
if ( index == value. getResultCode ( ) ) {
return value;
}
}
return null ;
}
}
CyclicBarrier
简述
允许一组线程全部等待彼此达到共同屏障点的同步辅助。 循环阻塞在涉及固定大小的线程方的程序中很有用 ,这些线程必须偶尔等待彼此 。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用 。它会在某个代码段后设置 await 方法,只有执行到当前 await 方法的代码段的线程达到一定数量才会解除阻塞状态运行别的。
构造函数
创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。
public CyclicBarrier ( int parties, Runnable barrierAction) {
if ( parties <= 0 ) throw new IllegalArgumentException ( ) ;
this . parties = parties;
this . count = parties;
this . barrierCommand = barrierAction;
}
await方法
public int await ( ) throws InterruptedException , BrokenBarrierException {
try {
return dowait ( false , 0L ) ;
} catch ( TimeoutException toe) {
throw new Error ( toe) ;
}
}
private int dowait ( boolean timed, long nanos)
throws InterruptedException , BrokenBarrierException ,
TimeoutException {
final ReentrantLock lock = this . lock;
lock. lock ( ) ;
try {
final Generation g = generation;
if ( g. broken)
throw new BrokenBarrierException ( ) ;
if ( Thread . interrupted ( ) ) {
breakBarrier ( ) ;
throw new InterruptedException ( ) ;
}
int index = -- count;
if ( index == 0 ) {
boolean ranAction = false ;
try {
final Runnable command = barrierCommand;
if ( command != null )
command. run ( ) ;
ranAction = true ;
nextGeneration ( ) ;
return 0 ;
} finally {
if ( ! ranAction)
breakBarrier ( ) ;
}
}
for ( ; ; ) {
try {
if ( ! timed)
trip. await ( ) ;
else if ( nanos > 0L )
nanos = trip. awaitNanos ( nanos) ;
} catch ( InterruptedException ie) {
if ( g == generation && ! g. broken) {
breakBarrier ( ) ;
throw ie;
} else {
Thread . currentThread ( ) . interrupt ( ) ;
}
}
if ( g. broken)
throw new BrokenBarrierException ( ) ;
if ( g != generation)
return index;
if ( timed && nanos <= 0L ) {
breakBarrier ( ) ;
throw new TimeoutException ( ) ;
}
}
} finally {
lock. unlock ( ) ;
}
}
案例
package JUC. api;
import java. util. concurrent. BrokenBarrierException ;
import java. util. concurrent. CyclicBarrier ;
public class CyclicBarrierDemo {
public static void main ( String [ ] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier ( 7 , ( ) -> {
System . out. println ( "召唤神龙!" ) ;
} ) ;
for ( int i = 1 ; i < 8 ; i++ ) {
int temp = i;
new Thread ( ( ) -> {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 收集到第 " + temp + "颗龙珠" ) ;
try {
cyclicBarrier. await ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} catch ( BrokenBarrierException e) {
e. printStackTrace ( ) ;
}
} , String . valueOf ( i) ) . start ( ) ;
}
}
}
Semaphore
简述
一个计数信号量。 在概念上,信号量维持一组许可证 。 如果有必要,每个acquire()阻塞 ,直到许可证可用 ,然后才能使用它。 每个release()添加许可证 ,潜在地释放阻塞获取方 。 但是,没有使用实际的许可证对象 Semaphore 只保留可用数量的计数 ,并相应地执行就可以。 一般用在多个线程抢多个资源的情况。 可以变相替代 synchronized 和 lock 。
构造函数
public Semaphore ( int permits , boolean fair) {
sync = fair ? new FairSync ( permits ) : new NonfairSync ( permits ) ;
}
acquire方法
public void acquire ( ) throws InterruptedException {
sync. acquireSharedInterruptibly ( 1 ) ;
}
public final void acquireSharedInterruptibly ( int arg)
throws InterruptedException {
if ( Thread . interrupted ( ) )
throw new InterruptedException ( ) ;
if ( tryAcquireShared ( arg) < 0 )
doAcquireSharedInterruptibly ( arg) ;
}
private void doAcquireSharedInterruptibly ( int arg)
throws InterruptedException {
final Node node = addWaiter ( Node . SHARED) ;
boolean failed = true ;
try {
for ( ; ; ) {
final Node p = node. predecessor ( ) ;
if ( p == head) {
int r = tryAcquireShared ( arg) ;
if ( r >= 0 ) {
setHeadAndPropagate ( node, r) ;
p. next = null ;
failed = false ;
return ;
}
}
if ( shouldParkAfterFailedAcquire ( p, node) &&
parkAndCheckInterrupt ( ) )
throw new InterruptedException ( ) ;
}
} finally {
if ( failed)
cancelAcquire ( node) ;
}
}
案例
信号量控制的代码段为:
semaphore.acquire() 与 semaphore.release() 中间的那一段代码段。
package JUC. api;
import java. util. concurrent. Semaphore ;
import java. util. concurrent. TimeUnit ;
public class SemaphoreDemo {
public static void main ( String [ ] args) {
Semaphore semaphore = new Semaphore ( 5 , false ) ;
for ( int i = 1 ; i <= 10 ; i++ ) {
new Thread ( ( ) -> {
try {
semaphore. acquire ( ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "车\t抢到车位。。。" ) ;
TimeUnit . SECONDS. sleep ( 3 ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "车\t停车三秒后离开车位!" ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
} finally {
semaphore. release ( ) ;
}
} , String . valueOf ( i) ) . start ( ) ;
}
}
}
阻塞队列
什么是阻塞队列
顾名思义是一个队列。 当阻塞队列是空的时候 ,线程 从队列中获取 元素的操作将会被阻塞。当阻塞队列是满的时候 ,线程 往队列里添加 元素的操作将会被阻塞。很像生产者消费者 模型。
|
线程一往阻塞队列中添加元素,线程二往阻塞队列中移除元素
阻塞队列有没有好的一面 ?
在 JUC 发布之前,多线程环境下,我们每个开发者都得自己去控制什么时候阻塞线程什么时候唤醒线程,尤其是要兼顾效率和线程安全,这样做会给程序带来很大的复杂度,容易出错,不好测试。 我们不用关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切都被阻塞队列包了。 在多线程领域:
所谓阻塞,就是某些情况下会挂起线程,一旦条件满足,被挂起的线程又会被自动唤醒。 不得不阻塞的话如何管理?
ArrayBlockingQueue
简述
它是一个基于数组结构 的有界限的阻塞队列。 此队列按照 **FIFO(先进先出)**原则对元素进行排序。
LinkedBlockingQueue
简述
一个基于链表结构 的阻塞队列,此队列按照 **FIFO(先进先出)**排序元素。 有界的,默认大小为 int 类型的最大值。 吞吐量通常要高于 ArrayBlockingQueue 。
SynchronousQueue
简述
一个不存储元素 的阻塞队列。 每个插入操作必须等待另一个线程调用移除操作 。 每个 put() 对应一个 take() 。 否则插入操作一直处于阻塞状态,吞吐量通常高于 LinkedBlockingQueue 。 慎用。
示例
package JUC. block;
import java. util. concurrent. BlockingQueue ;
import java. util. concurrent. SynchronousQueue ;
import java. util. concurrent. TimeUnit ;
public class SynchronousQueueDemo {
public static void main ( String [ ] args) {
BlockingQueue < String > strings = new SynchronousQueue < > ( ) ;
new Thread ( ( ) -> {
try {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 添加 1 " ) ;
strings. put ( "1" ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 添加 2 " ) ;
strings. put ( "2" ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 添加 3 " ) ;
strings. put ( "3" ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} , "A" ) . start ( ) ;
new Thread ( ( ) -> {
try {
TimeUnit . SECONDS. sleep ( 5 ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 取出 1 " ) ;
strings. take ( ) ;
TimeUnit . SECONDS. sleep ( 5 ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 取出 2 " ) ;
strings. take ( ) ;
TimeUnit . SECONDS. sleep ( 5 ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "线程\t 取出 3 " ) ;
strings. take ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} , "B" ) . start ( ) ;
}
}
其它阻塞队列
PriorityBlockingQueue
DelayQueue
LinkedTransferQueue
LinkedBlockingDeque
BlockingQueue的核心方法
方法类型 抛出异常 特殊值 阻塞 超时 插入 add(e) offer(e) put(e) offer(e,time,unit) 移除 remove() poll() take() poll(time,unit) 检查 element() peek() 不可用 不可用
抛出异常 当阻塞队列满了的时候,再往队列里 add数据 会抛出IllegalStateException:Queue full异常,当阻塞队列空的时候,再往队列里 remove 数据会抛出NoSuchElementException异常。 特殊值 插入方法,成功返回true 失败返回false ;移除方法,成功返回出队元素 ,队列里面没有就返回null 。 一直阻塞 当阻塞队列满的时候,生产者 线程继续往队列里 put 元素 的话,队列会一直阻塞生产线程直到 put 数据或者响应中断退出 。当阻塞队列空 的时候,消费者 线程试图从队列里 take 元素的话,队列会一直阻塞消费者线程直到队列可用。 超时 过时不候
抛出异常组测试
测试了 BlockingQueue 中会抛出异常 的三个方法的使用。
package JUC. api;
import java. util. concurrent. ArrayBlockingQueue ;
import java. util. concurrent. BlockingQueue ;
public class BlockingQueueDemo {
public static void main ( String [ ] args) throws Exception {
System . out. println ( "--------------------初始容量为三插入三个数据-----------------------" ) ;
BlockingQueue < String > blockingQueue = new ArrayBlockingQueue < > ( 3 ) ;
System . out. println ( blockingQueue. add ( "a" ) ) ;
System . out. println ( blockingQueue. add ( "b" ) ) ;
System . out. println ( blockingQueue. add ( "c" ) ) ;
System . out. println ( "----------------------容量刚好到三---------------------" ) ;
System . out. println ( ) ;
try {
System . out. println ( "----------------------添加第四个数据越界抛出异常---------------------" ) ;
System . out. println ( blockingQueue. add ( "d" ) ) ;
System . out. println ( ) ;
} catch ( IllegalStateException e) {
e. printStackTrace ( ) ;
}
System . out. println ( "检查一下当前元素空不空,不空的话队首元素是:" + blockingQueue. element ( ) ) ;
System . out. println ( ) ;
System . out. println ( "--------------------移除三个数据-----------------------" ) ;
System . out. println ( blockingQueue. remove ( ) ) ;
System . out. println ( blockingQueue. remove ( ) ) ;
System . out. println ( blockingQueue. remove ( ) ) ;
System . out. println ( ) ;
try {
System . out. println ( "--------------------移除第四个数据发现抛出异常-----------------------" ) ;
System . out. println ( blockingQueue. remove ( ) ) ;
System . out. println ( ) ;
} catch ( IllegalStateException e) {
e. printStackTrace ( ) ;
}
}
}
返回布尔值组(特殊值组)测试
测试了 BlockingQueue 中会返回特殊值 的三个方法的使用。
package JUC. block;
import java. util. concurrent. ArrayBlockingQueue ;
import java. util. concurrent. BlockingQueue ;
public class BlockingQueueDemo {
public static void main ( String [ ] args) throws Exception {
System . out. println ( "--------------------初始容量为三插入三个数据-----------------------" ) ;
BlockingQueue < String > blockingQueue = new ArrayBlockingQueue < > ( 3 ) ;
System . out. println ( blockingQueue. offer ( "a" ) ) ;
System . out. println ( blockingQueue. offer ( "b" ) ) ;
System . out. println ( blockingQueue. offer ( "c" ) ) ;
System . out. println ( "----------------------容量刚好到三---------------------" ) ;
System . out. println ( "尝试插入第四个值,返回插入结果:" + blockingQueue. offer ( "d" ) ) ;
System . out. println ( "检查一下当前元素空不空,不空的话队首元素是:" + blockingQueue. peek ( ) ) ;
System . out. println ( ) ;
System . out. println ( "----------------------取元素---------------------" ) ;
System . out. println ( blockingQueue. poll ( ) ) ;
System . out. println ( blockingQueue. poll ( ) ) ;
System . out. println ( blockingQueue. poll ( ) ) ;
System . out. println ( blockingQueue. poll ( ) ) ;
}
}
阻塞组测试
测试了 BlockingQueue 中会阻塞值 的两个方法的使用。 由于这个方法在多线程下可能更直观,于是就换成了多线程的写法。
package JUC. block;
import java. util. concurrent. ArrayBlockingQueue ;
import java. util. concurrent. BlockingQueue ;
public class BlockingQueueDemo {
public static void main ( String [ ] args) throws Exception {
BlockingQueue < String > blockingQueue = new ArrayBlockingQueue < > ( 3 ) ;
for ( int i = 1 ; i <= 5 ; i++ ) {
new Thread ( ( ) -> {
try {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 尝试放入数据。。。" ) ;
blockingQueue. put ( "a" ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 放入数据成功!" ) ;
} , String . valueOf ( i) ) . start ( ) ;
}
for ( int i = 1 ; i <= 5 ; i++ ) {
new Thread ( ( ) -> {
try {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 尝试取出数据。。。" ) ;
blockingQueue. take ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 取出数据成功!" ) ;
} , String . valueOf ( i) ) . start ( ) ;
}
}
}
放入成功 的提示语句不可能有三个以上同时打印出来 。取出成功 的提示语句同样也不可能有三个以上同时又打印出来 。每个线程都打印了四条不同的提示语句 ,与预期线程安全期望 结果一致。
超时组测试
每个线程都打印了两条不同的提示语句 ,与预期线程安全期望 结果一致。
package JUC. block;
import java. util. concurrent. ArrayBlockingQueue ;
import java. util. concurrent. BlockingQueue ;
import java. util. concurrent. TimeUnit ;
public class BlockingQueueDemo {
public static void main ( String [ ] args) throws Exception {
BlockingQueue < String > blockingQueue = new ArrayBlockingQueue < > ( 3 ) ;
for ( int i = 1 ; i <= 5 ; i++ ) {
new Thread ( ( ) -> {
try {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 放入数据成功!"
+ blockingQueue. offer ( "a" , 5 , TimeUnit . MICROSECONDS) ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} , String . valueOf ( i) ) . start ( ) ;
}
for ( int i = 1 ; i <= 5 ; i++ ) {
new Thread ( ( ) -> {
try {
System . out. println ( Thread . currentThread ( ) . getName ( ) + "\t 取出数据成功!"
+ blockingQueue. poll ( 10 , TimeUnit . MICROSECONDS) ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
} , String . valueOf ( i) ) . start ( ) ;
}
}
}
码云仓库同步笔记,可自取欢迎各位star指正:https://gitee.com/noblegasesgoo/notes
如果出错希望评论区大佬互相讨论指正,维护社区健康大家一起出一份力,不能有容忍错误知识。
—————————————————————— 爱你们的 noblegasesgoo