并发工具类CountDownLatch、CyclicBarrier(同步屏障)、Semaphore(控制并发线程数)、Exchanger(线程交换数据)
CountDownLatch
简介原理
CountDownLatch是由队列同步器实现的。
在构建新的CountDownLatch对象时,需要传入一个大于0的整形int参数。而这个参数就作为队列同步器的state同步状态了。
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
Sync(int count) { setState(count); }
当一个线程在执行了CountDownLatch的 .await() 方法之后,会阻塞自旋判断state是否是0。而state是通过CountDownLatch的 .countDown() 方法来减少的。
public void countDown() { sync.releaseShared(1); }
用法场景
比如需要多线程汇算多个sheet,最后需要结果汇总,就可以在主线程那里执行await(),在每个sheet运算完了之后执行countDown()即可
CyclicBarrier
简介原理
CyclicBarrier同步屏障有两种构造方法。
第一种,参数只传整形数字。
public CyclicBarrier(int parties) { this(parties, null); }
第二种,参数还要传一个线程的实现
public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }
CyclicBarrier是通过调用其 .await() 方法,对当前线程进行上锁,其内部有成员变量可重入锁。
第一种:
1.假设参数传2,线程执行await()方法,线程1先抢到锁,然后会对parties参数先减1,然后判断是否是0,是0则运行线程后面的逻辑了。
2.如果不是0,则调用Condition的trip()方法,trip()方法会释放锁并等待。这时候线程2就可以抢到锁,然后执行第1步里的逻辑,这时候parties减1就是0了。这时候会调用Condition的signalAll()方法,通知trip()方法的线程可以继续执行了
第二种:
参数不仅传2,还传了另一个线程(别名取C)进来。其实大致逻辑和第一种差不多,只是在第1步判断了parties为0之后,会先去执行线程C的逻辑,再去唤醒另外两个线程。
用法场景
CountDownLatch与CyclicBarrier的区别
个人理解, CountDownLatch 就像是军训教官让学员报数,等所有人(线程)报完数了之后,教官在他的小本本上记录人数并开始训话(. .await() )。而 CyclicBarrier 也像是军训让学员报数,而报数完了之后,学员立即去完成各自的任务,学员等待他人报数的过程就是从 .await() 开始的
Semaphore
简介原理
构建Semaphore的时候需要整形参数,表示它最多允许多少个线程并行运行。
public Semaphore(int permits) { sync = new NonfairSync(permits); }
线程中需要执行 semaphore.acquire() 表示拿到许可证了,然后同步状态会响应变化,然后执行 sync.releaseShared(1) 表示释放许可证,其它线程可以获取许可证了
用法场景
各种需要限制线程数量的场景。例如连接池场景,需要限制线程数。
Exchanger
用法场景
线程间交换数据