Описание CountDownLatch
Обзор защелки обратного отсчета
-
CountDownLatch
Обычно используется как счетчик обратного отсчета для многопоточности, заставляя их ждать другую группу (CountDownLatch
Решение об инициализации) выполнение задачи завершено. - Следует отметить, что
CountDownLatch
После инициализации, когда значение счетчика уменьшается до 0, его нельзя восстановить.Semaphore
,Semaphore
да черезrelease
Операция восстанавливает семафор.
Как работает CountDownLatch
Принцип использования
- Создайте CountDownLatch и установите значение счетчика.
- Запустите многопоточность и вызовите метод countDown() экземпляра CountDownLatch.
- вызов основного потока
await()
метод, так что работа основного потока будет блокироваться в этом методе до тех пор, пока другие потоки не завершат свои соответствующие задачи, значение счетчика равно 0, прекратит блокировку, а основной поток продолжит выполнение.
Используйте шаблоны
public class CountDownLatchModule {
//线程数
private static int N = 10;
// 单位:min
private static int countDownLatchTimeout = 5;
public static void main(String[] args) {
//创建CountDownLatch并设置计数值,该count值可以根据线程数的需要设置
CountDownLatch countDownLatch = new CountDownLatch(N);
//创建线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < N; i++) {
cachedThreadPool.execute(() ->{
try {
System.out.println(Thread.currentThread().getName() + " do something!");
} catch (Exception e) {
System.out.println("Exception: do something exception");
} finally {
//该线程执行完毕-1
countDownLatch.countDown();
}
});
}
System.out.println("main thread do something-1");
try {
countDownLatch.await(countDownLatchTimeout, TimeUnit.MINUTES);
} catch (InterruptedException e) {
System.out.println("Exception: await interrupted exception");
} finally {
System.out.println("countDownLatch: " + countDownLatch.toString());
}
System.out.println("main thread do something-2");
//若需要停止线程池可关闭;
// cachedThreadPool.shutdown();
}
результат операции:
main thread do something-1
pool-1-thread-1 do something!
pool-1-thread-2 do something!
pool-1-thread-3 do something!
pool-1-thread-5 do something!
pool-1-thread-6 do something!
pool-1-thread-7 do something!
pool-1-thread-8 do something!
pool-1-thread-4 do something!
pool-1-thread-9 do something!
pool-1-thread-10 do something!
countDownLatch: java.util.concurrent.CountDownLatch@76fb509a[Count = 0]
main thread do something-2
Общие методы CountDownLatch
-
public void await() throws InterruptedException
:перечислитьawait()
Поток метода будет приостановлен, ожидая, покаcount
значение0
Продолжайте выполнять снова. -
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
:такой жеawait()
Если ожиданиеtimeout
После долгого времени,count
Значение по-прежнему не изменилось на 0, больше не ждите, продолжайте выполнять. В качестве единиц времени обычно используются миллисекунды, дни, часы, микросекунды, минуты, наносекунды, секунды. -
public void countDown()
: значение счетчика уменьшается на 1. -
public long getCount()
: Получите текущую стоимость подсчета. -
public String toString()
: переопределить метод toString() и вывести больше значений счетчика. Подробности см. в исходном коде.
Сценарии использования CountDownLatch
В программе выполняется N задач, мы можем создать CountDownLatch со значением N, и когда каждая задача будет завершена, вызватьcountDown()
метод уменьшенияcount值
, а затем использовать его в основном потокеawait()
Метод ожидает завершения выполнения задачи, и основной поток продолжает выполнение.
Исходный код CountDownLatch
Исходный код конструктора
/**
* Constructs a {@code CountDownLatch} initialized with the given count.
*
* @param count the number of times {@link #countDown} must be invoked
* before threads can pass through {@link #await}
* @throws IllegalArgumentException if {@code count} is negative
*/
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
Исходный код метода toString()
/**
* Returns a string identifying this latch, as well as its state.
* The state, in brackets, includes the String {@code "Count ="}
* followed by the current count.
*
* @return a string identifying this latch, as well as its state
*/
public String toString() {
return super.toString() + "[Count = " + sync.getCount() + "]";
}
Пример обратного отсчета
как сигнал запуска потока
код
public class CountDownLatchTest {
/**
* a start signal that prevents any worker from proceeding
* until the driver is ready for them to proceed;
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
// create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
}
// don't let run yet
System.out.println("do something else 1");
// let all threads proceed
startSignal.countDown();
System.out.println("do something else 2");
// wait for all to finish
doneSignal.await();
System.out.println("wait for all to finsh");
}
static class Worker implements Runnable{
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
результат операции
do something else 1
do something else 2
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
do work!
wait for all to finsh
Из бегущих результатов видно, что:
- Основной поток печатается первым
do something else 1
иdo something else 2
. так какstartSignal.countDown();
После того, как счетчик станет равным 0, дочерний поток может печатать. - так как
startSignal.await();
находится внутри дочернего потока, все дочерние потоки ожидаютstartSignal.countDown()
После выполнения для печатиdo work!
. -
doneSignal.await();
После ожидания выполнения всех дочерних потоков каждый разdoneSignal.countDown()
, последний счетчик равен 0, основной поток выполняет печатьwait for all to finsh
.
Пока поток ожидает сигнала завершения
код
public class CountDownLatchTest2 {
/**
* a completion signal that allows the driver to wait
* until all workers have completed.
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
// create and start threads
cachedThreadPool.execute(new Worker(doneSignal, i));
}
// don't let run yet
System.out.println("do something else 1");
// wait for all to finish
doneSignal.await();
System.out.println("===========================count: " + doneSignal.getCount());
System.out.println("do something else 2");
cachedThreadPool.shutdown();
}
static class Worker implements Runnable{
private final CountDownLatch doneSignal;
private final int i;
Worker(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
@Override
public void run() {
try {
doWork();
doneSignal.countDown();
System.out.println("i = " + i + ", " + doneSignal.toString());
} catch (Exception ex) {
ex.printStackTrace();
}
}
void doWork() {
System.out.println("do work!");
}
}
}
результат операции
do something else 1
do work!
i = 0, java.util.concurrent.CountDownLatch@128abd43[Count = 4]
do work!
i = 1, java.util.concurrent.CountDownLatch@128abd43[Count = 3]
do work!
i = 2, java.util.concurrent.CountDownLatch@128abd43[Count = 2]
do work!
i = 3, java.util.concurrent.CountDownLatch@128abd43[Count = 1]
do work!
i = 4, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
===========================count: 0
do something else 2
do work!
i = 5, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 6, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 7, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 8, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
do work!
i = 9, java.util.concurrent.CountDownLatch@128abd43[Count = 0]
Из результатов выполнения видно, что основной поток ожидает выполнения других потоков 5 раз перед печатью.do something else 2
информацию, поскольку значение CountDownLatch равно 5.