Java併發之CyclicBarrier & CountDownLatch
CountDownLatch
“同生共死” 用於保持線程間的同時開始,初始化一個整形的信號量,每個信號再開始之前 使信號量減一,當信號量不爲0時,所有線程被阻塞,直到最後一個線程使信號量爲0。使其達到線程同時發生。相比 Semaphore,CountDownLatch除了控制線程併發數,也能保證線程同時執行。
- 操作
- CountDownLatch(int count) 並發信號量數目
- await() 永久阻塞直到信號量爲零
- await(long timeout, TimeUnit unit) 阻塞到最大延長時間或者信號量爲零
- countDown() 信號量減一
CyclicBarrier
“兄弟先上,我殿後”,類似CountDownLatch ,包含其所有功能,另實現了,同步完成後,執行的特定任務。
- 操作
- CyclicBarrier(int parties) 功能同CountDownLatch
- CyclicBarrier(int parties, Runnable barrierAction)
- await() 永久阻塞直到信號量爲零
- await(long timeout, TimeUnit unit) 阻塞到最大延長時間(會拋出錯誤提示,但會繼續執行)或者信號量爲零
CODE
package com.liaojl.test.concurrent;
import lombok.SneakyThrows;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
public class Test2 {
private static final Logger log = LoggerFactory.getLogger(Test2.class);
public static final int SIZE = 5;
/**
* 同生共死
*
* @throws InterruptedException
*/
@Test
public void countDownLatchTest() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
Thread thread = null;
for (int i = 0; i < SIZE + 1; i++) {
if (i > 1 && i < 3) {
thread = new Thread(new MyThread(countDownLatch, 1));
} else {
thread = new Thread(new MyThread(countDownLatch));
}
thread.setName("線程" + (i + 1));
thread.start();
}
Thread.sleep(100);
log.info("傻了吧,阻塞住了");
Thread.sleep(100);
log.info("開始了");
countDownLatch.countDown();
Thread.sleep(100);
}
private static class MyThread implements Runnable {
private static final Logger log = LoggerFactory.getLogger(MyThread.class);
private final CountDownLatch countDownLatch;
private long timeout = 0;
public MyThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public MyThread(CountDownLatch countDownLatch, long timeout) {
this.countDownLatch = countDownLatch;
this.timeout = timeout;
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@SneakyThrows
@Override
public void run() {
/**
* 線程阻塞
*/
if (timeout < 1) {
countDownLatch.await();
} else {
countDownLatch.await(timeout, TimeUnit.NANOSECONDS);
log.info("lz受不了,先走了");
}
log.info("線程名:{} 總數:{},時間:{} 一起去打怪!!!", Thread.currentThread().getName(), countDownLatch.getCount(), System.currentTimeMillis());
}
}
/**
* 有困難兄弟線上,我收人頭
*/
@Test
public void cyclicBarrierTest() throws InterruptedException {
// CyclicBarrier cyclicBarrier = new CyclicBarrier(SIZE);
CyclicBarrier cyclicBarrier = new CyclicBarrier(SIZE,
() -> log.info("線程名:{} , 時間:{} 坐等收人頭!!!",
Thread.currentThread().getName(),
System.currentTimeMillis()));
Thread thread = null;
/**
* 當最後一個 await 完成 阻塞才完成
* true 最先執行 但後續的變爲同步任務,他們要同步執行完成
* false 最後執行,其執行順序爲隨意,任務的執行時間 是他們最後一個完成await 的時間
*/
boolean iAmFirst = false;
for (int i = 0; i < SIZE; i++) {
if (2 > i && i < 4) {
/**
* 當阻塞超時是 拋出錯誤異常,但不會影響後續執行,僅僅是錯誤提醒
* <code>
* Exception in thread "線程2" java.util.concurrent.TimeoutException
* at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
* at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
* at com.liaojl.test.concurrent.Test2$MyThread2.run(Test2.java:180)
* at java.lang.Thread.run(Thread.java:748)
* </code>
*/
thread = new Thread(new MyThread2(cyclicBarrier, 1, iAmFirst));
} else {
thread = new Thread(new MyThread2(cyclicBarrier, iAmFirst));
}
thread.setName("線程" + (i + 1));
thread.start();
}
Thread.sleep(5000);
}
private static class MyThread2 implements Runnable {
private static final Logger log = LoggerFactory.getLogger(MyThread.class);
private final CyclicBarrier cyclicBarrier;
private boolean first;
private long timeout = 0;
/**
* @param cyclicBarrier
* @param first 是否最先執行
*/
public MyThread2(CyclicBarrier cyclicBarrier, boolean first) {
this.cyclicBarrier = cyclicBarrier;
this.first = first;
}
/**
* @param cyclicBarrier
* @param timeout 延遲時間 納秒
* @param first 是否最先執行
*/
public MyThread2(CyclicBarrier cyclicBarrier, long timeout, boolean first) {
this.cyclicBarrier = cyclicBarrier;
this.timeout = timeout;
this.first = first;
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
@SneakyThrows
@Override
public void run() {
long millis = (long) (Math.random() * 1000);
Thread.sleep(millis);
if (first) {
/**
* 線程阻塞
*/
if (timeout < 1) {
cyclicBarrier.await();
} else {
cyclicBarrier.await(timeout, TimeUnit.NANOSECONDS);
log.info("線程名:{} lz不當炮灰,先走了!!!",
Thread.currentThread().getName());
}
}
log.info("線程名:{} 總數:{},到達屏障總數:{},時間:{} 一起去打怪!!!",
Thread.currentThread().getName(),
cyclicBarrier.getParties(),
cyclicBarrier.getNumberWaiting(),
System.currentTimeMillis());
if (!first) {
/**
* 線程阻塞
*/
if (timeout < 1) {
cyclicBarrier.await();
} else {
cyclicBarrier.await(timeout, TimeUnit.NANOSECONDS);
log.info("線程名:{} lz不當炮灰,先走了!!!",
Thread.currentThread().getName());
}
}
}
}
}