CountDownLatch类简介
先看看1.8源码里面对CountDownLatch的描述:
-
A synchronization aid that allows one or more threads to wait until
-
a set of operations being performed in other threads completes.
-
A {@code CountDownLatch} is initialized with a given count.
-
The {@link #await await} methods block until the current count reaches
-
zero due to invocations of the {@link #countDown} method, after which
-
all waiting threads are released and any subsequent invocations of
-
{@link #await await} return immediately. This is a one-shot phenomenon
-
– the count cannot be reset. If you need a version that resets the
-
count, consider using a {@link CyclicBarrier}.
-
A {@code CountDownLatch} is a versatile synchronization tool
-
and can be used for a number of purposes. A
-
{@code CountDownLatch} initialized with a count of one serves as a
-
simple on/off latch, or gate: all threads invoking {@link #await await}
-
wait at the gate until it is opened by a thread invoking {@link
-
#countDown}. A {@code CountDownLatch} initialized to N
-
can be used to make one thread wait until N threads have
-
completed some action, or some action has been completed N times.
-
A useful property of a {@code CountDownLatch} is that it
-
doesn’t require that threads calling {@code countDown} wait for
-
the count to reach zero before proceeding, it simply prevents any
-
thread from proceeding past an {@link #await await} until all
-
threads could pass.
-
Sample usage: Here is a pair of classes in which a group
-
of worker threads use two countdown latches:
- The first is a start signal that prevents any worker from proceeding
-
until the driver is ready for them to proceed;
- The second is a completion signal that allows the driver to wait
-
until all workers have completed.
英文有点长意思总结来说就是: 在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。
1. 准备工作线程
这里引用了源码里面的示例。目的是用来获取一个单例
public class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
public Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await();//当前线程调用并阻塞在await()方法(直至startSignal为0)
doWork();
doneSignal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
void doWork() {
System.out.println("我是线程: " + Thread.currentThread().getName() + " 当前取得的单例为: " + Singleton.getInstance());
}
}
2.创建测试类
public class ThreadTest {
public static void main(String[] args) throws InterruptedException {
int threadNum = 10;
CountDownLatch startSingle = new CountDownLatch(1);
CountDownLatch doneSingle = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
new Thread(new Worker(startSingle, doneSingle)).start();
}
startSingle.countDown();//释放阻塞在startSignal.await()方法的线程,让他们开始执行
System.out.println("开始释放所有线程");
doneSingle.await();//阻塞主进程直至doneSingle为0,开始执行打印111
System.out.println("执行主进程打印任务");
}
}
3.准备单例类
3.1 线程不安全类(懒汉式)
public class Singleton {
private Singleton() {
}
private static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
打印如下
开始释放所有线程
我是线程: Thread-7 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-1 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-0 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-5 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-3 当前取得的单例为: com.dingshun.current.Singleton@5dd54814
我是线程: Thread-4 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-8 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-9 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-6 当前取得的单例为: com.dingshun.current.Singleton@3195835c
我是线程: Thread-2 当前取得的单例为: com.dingshun.current.Singleton@3195835c
执行主进程打印任务
可见, 该单例模式的if null判断可能有多个线程跑进去,导致实例对象结果并不唯一,当10个线程并发获取单例对象,"单例"对象被示例化了两次
3.2 线程安全类(双重检测)
public class TwiceCheckSingleton {
private static volatile TwiceCheckSingleton singleton;
private TwiceCheckSingleton() {
}
public static TwiceCheckSingleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new TwiceCheckSingleton();
}
}
}
return singleton;
}
}
在测试类中,把工作线程的dowork()方法改成获取TwiceCheckSingleton
打印如下
开始释放所有线程
我是线程: Thread-4 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-2 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-8 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-0 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-5 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-6 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-3 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-9 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-1 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
我是线程: Thread-7 当前取得的单例为: com.dingshun.current.TwiceCheckSingleton@35d7e55a
执行主进程打印任务
这里的方法是线程安全的,无论执行多少次,每个线程获取的对象都是一样的。