详解Java CountDownLatch和CyclicBarrier
在多线程编程中,我们常常会用到Java中的并发工具类CountDownLatch
和CyclicBarrier
,它们都是用于线程同步的一种工具。本文将从内部实现和场景上的区别来详细讲解这两种工具类。
CountDownLatch
CountDownLatch
在多线程中被用于等待一个或多个事件完成后再执行某种操作。它的工作原理是:一个计数器被初始化为某个数值,只要每个事件完成,计数器就会减1。当计数器的值为0时,代表所有事件已完成,此时等待线程可以继续执行。
下面是一个示例,展示了CountDownLatch的使用:
import java.util.concurrent.CountDownLatch;
public class Example {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> {
try {
System.out.println("Thread 1 is running");
Thread.sleep(1000);
System.out.println("Thread 1 is done");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
System.out.println("Thread 2 is running");
Thread.sleep(2000);
System.out.println("Thread 2 is done");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
try {
System.out.println("Waiting for threads to finish");
latch.await();
System.out.println("All threads are done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果如下:
Waiting for threads to finish
Thread 1 is running
Thread 2 is running
Thread 1 is done
Thread 2 is done
All threads are done
在此示例中,我们创建了一个CountDownLatch
对象,并将其初始化为2。然后,我们创建了两个线程,让它们执行一些耗时的操作,最后等待线程会在调用await()
方法时阻塞,直到两个线程将计数器减为0,然后输出"All threads are done"。
CyclicBarrier
CyclicBarrier
也是用于线程同步的一种工具。它跟CountDownLatch
最大的区别是:CountDownLatch
的计数器在减到0之后就不能再用了,而CyclicBarrier
的计数器在减到0之后可以被重置,因此它也被称为“可循环屏障”。
CyclicBarrier
的工作原理是:创建一个CyclicBarrier
对象时需要指定一个线程数和一个barrier action,当这个线程数的线程全部执行到barrier action之后,这些线程才会继续向下执行。
下面是一个示例,展示了CyclicBarrier
的使用:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Example {
public static void main(String[] args) {
final int THREADS_NUM = 3;
final CyclicBarrier barrier = new CyclicBarrier(THREADS_NUM, () -> {
System.out.println("All threads are done");
});
for (int i = 0; i < THREADS_NUM; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " is running");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " is done");
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
}
输出结果如下:
Thread-0 is running
Thread-1 is running
Thread-2 is running
Thread-2 is done
Thread-1 is done
Thread-0 is done
All threads are done
在此示例中,我们创建了一个CyclicBarrier
对象,并将其初始化为3,同时创建三个线程。每个线程都会执行一些耗时的操作,并调用await()
方法等待其他线程执行完毕。当三个线程都执行到barrier
,即计数器减为0时,会执行barrier action
,输出"All threads are done"。三个线程输出的顺序不一定是一致的。
区别
- 内部实现
CountDownLatch
采用了AQS队列,共享变量是一个int类型的计数器,每当一个线程完成它的工作后,计数器就会减1。当计数器的值变为0时,所有等待的线程都会被唤醒。而CyclicBarrier
使用了ReentrantLock和Condition和AQS队列实现,还有一个parties表示初始化时需要等待的线程个数,这个值保持不变。
- 应用场景
CountDownLatch
通常用于等待异步任务的结果返回,将结果合并后再执行接下来的操作。例如多个线程处理一堆请求,等这些线程全部处理完毕以后再将处理结果进行合并。CyclicBarrier
通常用于在多个线程执行过程中,等待其他线程同步完成后再继续执行后面的业务逻辑,例如多个线程写入数据,最后将数据合并在一起并输出。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别 - Python技术站