JDK1.6 CountDownLatch

介绍CountDownLatch

一种锁,称为闭锁。可以让一个或多个线程等待另外一个或多个线程执行完毕后再执行。
CountDownLatch是基于AQS构建,使用共享模式。
CountDownLatch中提供一个count值来表示要等待的(其他任务)完成次数,常规用法有两种:Count(1)和Count(N)。举个栗子,百米赛跑,N个选手,每个选手可以看成是一个线程。起跑前,选手准备(线程启动,然后在Count(1)上阻塞),当发令枪响后(相当于Count(1)闭锁释放),选手一起起跑(相当于线程通过Count(1)继续执行),当所有选手都通过终点(相当于Count(N)闭锁释放),然后再统计成绩。

源码分析

CountDownLatch#Sync

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* Synchronization control For CountDownLatch.
* Uses AQS state to represent count.
*/
private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;

Sync(int count) {
setState(count);
}

int getCount() {
return getState();
}

// 1
public int tryAcquireShared(int acquires) {
return getState() == 0? 1 : -1;
}

// 2
public boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}

标注代码分析

  1. 如果当前count为0,那么方法返回1,按照之前对AQS的分析,请求成功,并唤醒同步队列里下一个共享模式的线程(这里都是共享模式的)。如果当前count不为0,那么方法返回-1,请求失败,当前线程最终会被阻塞(之前会不止一次调用tryAcquireShared)。
  2. 如果count为0,返回false,相当于释放失败,因为此时闭锁处于开放状态,没有必要在打开。如果count不为0,递减count。最后如果count为0,说明关闭的闭锁打开了,那么返回true,后面会唤醒等待队列中的线程。如果count不为0,说明闭锁还是处于关闭状态,返回false。

CountDownLatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
private final Sync sync;

/**
* 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);
}

/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of two things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
// 1
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}

/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted},
* or the specified waiting time elapses.
*
* <p>If the current count is zero then this method returns immediately
* with the value {@code true}.
*
* <p>If the current count is greater than zero then the current
* thread becomes disabled for thread scheduling purposes and lies
* dormant until one of three things happen:
* <ul>
* <li>The count reaches zero due to invocations of the
* {@link #countDown} method; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>The specified waiting time elapses.
* </ul>
*
* <p>If the count reaches zero then the method returns with the
* value {@code true}.
*
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value {@code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return {@code true} if the count reached zero and {@code false}
* if the waiting time elapsed before the count reached zero
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
// 2
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
*
* <p>If the current count is greater than zero then it is decremented.
* If the new count is zero then all waiting threads are re-enabled for
* thread scheduling purposes.
*
* <p>If the current count equals zero then nothing happens.
*/
// 3
public void countDown() {
sync.releaseShared(1);
}

/**
* Returns the current count.
*
* <p>This method is typically used for debugging and testing purposes.
*
* @return the current count
*/
public long getCount() {
return sync.getCount();
}

/**
* 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() + "]";
}

标注代码分析

  1. 如果当前count=0,那么方法立即返回。 如果当前count!=0,那么当前线程会一直等待,直到count被(其他线程)减到0或者当前线程被中断。
  2. 如果当前count=0,那么方法立即返回true。如果当前count!=0,那么当前线程会一直等待,直到count被(其他线程)减到0或者当前线程被中断或者超时。
  3. 如果当前count大于0,递减count。如果递减后,count等于0,那么AQS中所有等待线程都被唤醒。如果当前count等于0,什么事都不会发生。

总结

  1. 建立一个count为n的闭锁后,闭锁的内部计数为n,这时如果有线程调用闭锁的await方法,会阻塞。
  2. 每一次调用闭锁的countDown方法,内部计数就会减1,当闭锁的countDown方法被调用n次后,内部计数减为0,这时在闭锁await方法上等待的线程就被唤醒了。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×