注意!注意!如果代码出现乱序,请双击代码部分,则会出现排序正常的代码
1.阻塞队列BlockingQueue
先理解Queue,Deque,BlockingQueue的概念。
Queue(队列):用于保存一组元素,不过在存取元素的时候必须遵循先进先出原则,队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作,进行插入操作的端称为队尾,进行删除操作的端称为队头,队列中没有元素时,称为空队列。在队列这种数据结构中,最先插入的元素将是最先被删除的元素;反之,最后插入的元素将是最后被删除的元素,因此队列又称为“先进先出”的线性表。
Deque(双端队列):两端都可以进出的队列。当我们约束从队列的一端进出队时,就形成了另外一种存取模式,它遵循先进先出原则,这就是栈结构。双端队列主要是用于栈操作。使用栈结构让操作有可追溯性(如windows窗口地址栏内的路径前进栈,后退栈)
阻塞队列是一个支持两个附加操作的队列。这两个附加操作是:在队列为空时,获取元素的线程会等待队列变为非空;当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿取元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿取元素。
阻塞队列提供了4种处理方法:

最新JDK中提供了7个阻塞队列,如下图所示:

Interface BlockingQueue<E>
BlockingQueue常用的方法有以下几种。

-
-
boolean | add(E e) Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available. 插入指定元素到队列中,如果可以在不违反容量限制的情况下立即这样做,然后返回true;否则,在没有空间可用的情况下,抛出IllegalStateException 异常 |
boolean | contains(Object o) Returns true if this queue contains the specified element. 返回true如果该队列包含指定元素 |
int | drainTo(Collection<? super E> c) Removes all available elements from this queue and adds them to the given collection. 将队列中所有可用的元素移除并添加到给定的集合中 |
int | drainTo(Collection<? super E> c, int maxElements) Removes at most the given number of available elements from this queue and adds them to the given collection. 将至多给定的可利用的元素数从队列中移除并添加到给定的集合中 |
boolean | offer(E e) Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and false if no space is currently available. 插入指定元素到队列中。如果可以在没有容量限制的情况下立即这样做。成功返回true,当前没有空间可利用的情况下返回false |
boolean | offer(E e, long timeout, TimeUnit unit) Inserts the specified element into this queue, waiting up to the specified wait time if necessary for space to become available. 插入给定的元素到队列中,如果不能立即插入,在给定的时间内等待可用空间 |
E | poll(long timeout, TimeUnit unit) Retrieves and removes the head of this queue, waiting up to the specified wait time if necessary for an element to become available. 检索并删除此队列的头,如果不能立即执行,则等待指定的等待时间以使元素变为可用。 |
void | put(E e) Inserts the specified element into this queue, waiting if necessary for space to become available. 插入指定元素到队列中,如果不能立即插入,则等待直到可行 |
int | remainingCapacity() Returns the number of additional elements that this queue can ideally (in the absence of memory or resource constraints) accept without blocking, or Integer.MAX_VALUE if there is no intrinsic limit. 返回此队列在理想情况下(在没有内存或资源限制的情况下)可以不阻塞地接受的附加元素的数目,或者Integer.MAX_值如果没有内在的限制。 |
boolean | remove(Object o) Removes a single instance of the specified element from this queue, if it is present. 从队列中删除指定元素的单个实例(如果存在)。 |
E | take() Retrieves and removes the head of this queue, waiting if necessary until an element becomes available. 检索并删除队列的头元素,如果不能立即执行,则等待直到元素可用 |
其中,BlockingQueue不接受null元素,试图add,put,或offer一个null元素时,某些实现会抛出NullPointerException异常。null被用作指示poll操作失败的警戒值
下面给出示例
消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | import java.util.concurrent.BlockingQueue;
class Consumer implements Runnable {
private int i=0;
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (i<10) {
consume(queue.take());
i++;
Thread.sleep(10L);
}
} catch (InterruptedException ex) { }
}
void consume(Object x) {
System.out.println("Consumer....start");
System.out.println((String)x);
System.out.println("Consumer...end");
}
}
|
|
生产者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
private int i=0;
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
try {
while (i<10) {
queue.put(produce());
System.out.println("Producer...end");
i++;
Thread.sleep(10L);
}
} catch (InterruptedException ex) { }
}
Object produce() {
System.out.println("Producer....");
return new String("test");
}
}
|
测试的Demo
1
2
3
4
5
6
7
8
9
10
11
12
13 | import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class Setup {
public static void main(String[] args) {
BlockingQueue<String> q = new LinkedBlockingQueue<String>(10);
Producer p = new Producer(q);
Consumer c1 = new Consumer(q);
Consumer c2 = new Consumer(q);
new Thread(p).start();
new Thread(c1).start();
new Thread(c2).start();
}
}
|
运行结果

2.数组阻塞队列ArrayBlockingQueue
ArrayBlockingQueue是一个由数组支持的有界的阻塞队列。此队列按FIFO(先进先出)原则对元素进行排序。队列的头部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。视图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性设置为true而构造的队列允许按照FIFO顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。
先来看一下ArrayBlockingQueue得部分源码,理解一下ArrayBlockingQueue的实现原理和机制
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 | public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
final Object[] items;
final ReentrantLock lock;
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
}
...
}
|
使用实例是
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 | import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
class TestB {
public static void main(String[] args) throws Exception{
final BlockingQueue<String> bq=new ArrayBlockingQueue<String>(16);
for(int i=0;i<4;i++){
new Thread(new Runnable(){
@Override
public void run(){
while(true){
try{
String log=(String)bq.take();
parseLog(log);
}catch(Exception e){
}
}
}
}).start();
}
for(int i=0;i<16;i++){
String log=(i+1)+"-->";
bq.put(log);
}
}
public static void parseLog(String log){
System.out.println(log+System.currentTimeMillis());
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
|
运行结果

3.链表阻塞队列LinkedBlockingQueue
LinkedBlockingQueue是基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回。只有当队列缓冲区达到最大值缓存容量时(LinkedBlockingQueue可以通过构造函数指定该值),才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒。反之,对于消费者这端的处理也基于同样的原理。而LinkedBlockingQueue之所以能够高效地处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
作为开发者,我们需要注意的是,如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.,MAX_VALUE),这样的话,如果生产者的速读一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统就有可能已被消耗殆尽了
先来看下LinkedBlockingQuque得部分源码,理解一下LinkedBlockingQuque的实现原理和机制。
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212 | public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private transient Node<E> last;
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
private final Condition notFull = putLock.newCondition();
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
notFull.await();
}
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
while (count.get() == capacity) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(new Node<E>(e));
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return true;
}
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
signalNotEmpty();
return c >= 0;
}
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
/**
* Unlinks interior Node p with predecessor trail.
*/
void unlink(Node<E> p, Node<E> trail) {
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
public boolean remove(Object o) {
if (o == null) return false;
fullyLock();
try {
for (Node<E> trail = head, p = trail.next;
p != null;
trail = p, p = p.next) {
if (o.equals(p.item)) {
unlink(p, trail);
return true;
}
}
return false;
} finally {
fullyUnlock();
}
}
...
}
|
这里使用实例与上一节中ArrayBlockingQueue的例子一样。不同的是,将ArrayBlockingQueue的例子换成LinkedBlockingQueue即可:
fianl BlockingQueue<String> bq=new ArrayBlockingQueue<String>(16);
换成:
fianl BlockingQueue<String> bq=new LinkedBlockingQueue<String>(16);
4.优先级阻塞队列PriorityBlockingQueue
PrioriityBlockingQueue是一个支持优先级排序的无界阻塞队列(优先级的判断通过构造函数传入的Compator来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者,因此使用的时候要特别注意,生产者生产数据的速度绝对不能快于消费者消费者消费的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。在实现PriorityBlockingQueue时,內部控制线程同步的锁采用的是公平锁。
构造函数
-
-
| Constructor and Description |
|---|
PriorityBlockingQueue() Creates a PriorityBlockingQueue with the default initial capacity (11) that orders its elements according to their natural ordering. 创建一个带有默认初始容量的根据它的自然顺序进行元素排序的优先级阻塞队列 |
PriorityBlockingQueue(Collection<? extends E> c) Creates a PriorityBlockingQueue containing the elements in the specified collection. 创建一个包含指定集合元素的优先级阻塞队列 |
PriorityBlockingQueue(int initialCapacity) Creates a PriorityBlockingQueue with the specified initial capacity that orders its elements according to their natural ordering. |
PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) Creates a PriorityBlockingQueue with the specified initial capacity that orders its elements according to the specified comparator. |
先看一下PriorityBlockingQueue的部分源码,理解一下ProrityBlockingQueue的实现原理和机制
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 | @SuppressWarnings("unchecked")
public class PriorityBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
private transient Comparator<? super E> comparator;
private final ReentrantLock lock;
private final Condition notEmpty;
public PriorityBlockingQueue(int initialCapacity,
Comparator<? super E> comparator) {
if (initialCapacity < 1)
throw new IllegalArgumentException();
this.lock = new ReentrantLock();
this.notEmpty = lock.newCondition();
this.comparator = comparator;
this.queue = new Object[initialCapacity];
}
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return dequeue();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null)
notEmpty.await();
} finally {
lock.unlock();
}
return result;
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
E result;
try {
while ( (result = dequeue()) == null && nanos > 0)
nanos = notEmpty.awaitNanos(nanos);
} finally {
lock.unlock();
}
return result;
}
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (size == 0) ? null : (E) queue[0];
} finally {
lock.unlock();
}
}
...
}
|
一个使用实例
实体类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | public class Entry{
private int comparenum;
private String name;
public Entry(int comparenum,String name){
this.comparenum=comparenum;
this.name=name;
}
public String getName(){
return name;
}
public int getCompareNum(){
return comparenum;
}
@Override
public String toString(){
return getName()+"[the num:"+getCompareNum()+"]";
}
}
|
实体类的比较器
1
2
3
4
5
6
7 | import java.util.Comparator;
public class EntryComparetor implements Comparator<Entry> {
@Override
public int compare(Entry o1, Entry o2) {
return o2.getCompareNum() - o1.getCompareNum();
}
}
|
生产者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import java.util.Random;
import java.util.concurrent.PriorityBlockingQueue;
public class ProducerRunnable implements Runnable{
private static final String name = "王孙朱丘明刚红李刘吕赵黄曾游丽吴昊周郑秦";
private Random random = new Random();
private PriorityBlockingQueue<Entry> queue;
public ProducerRunnable(PriorityBlockingQueue<Entry> queue) {
this.queue = queue;
}
@Override
public void run() {
for(int i = 0; i < 20; i ++){
Entry o1 = new Entry(random.nextInt(10000), "小" + name.charAt(i));
queue.put(o1);
System.out.println(o1 + " 开始排队...");
}
}
}
|
消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | import java.util.Random;
import java.util.concurrent.PriorityBlockingQueue;
public class ConsumerRunnable implements Runnable {
private PriorityBlockingQueue<Entry> queue;
public ConsumerRunnable(PriorityBlockingQueue<Entry> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true){
Entry take = queue.poll();
if (take == null){
break;
}
System.out.println(take + " 办理业务.");
}
}
}
|
测试Demo
1
2
3
4
5
6
7
8
9
10
11
12 | import java.util.Random;
import java.util.concurrent.PriorityBlockingQueue;
public class Testpriority{
public static void main(String[] args) throws InterruptedException {
PriorityBlockingQueue<Entry> queue = new PriorityBlockingQueue<Entry>(30, new EntryComparetor());
Thread thread = new Thread(new ProducerRunnable(queue));
thread.start();
thread.join();
new Thread(new ConsumerRunnable(queue)).start();
}
}
|
运行结果。可以看到随机加入,但取出按优先级大小

另一个例子,涉及线程池
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
130
131
132 | import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
public class TestB {
public static void main(String[] args) throws Exception {
Random random = new Random(47);
ExecutorService exec = Executors.newCachedThreadPool();//新建线程池
PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<>();
exec.execute(new PrioritizedTaskProducer(queue, exec)); // 生产者 这里需要注意,往 //PriorityBlockingQueue中添加任务和取出任务的
exec.execute(new PrioritizedTaskConsumer(queue)); // 消费者 步骤是同时进行的,因而输出结果并不一定是有序的
}
}
class PrioritizedTask implements Runnable, Comparable<PrioritizedTask> {
private Random random = new Random(47);
private static int counter = 0;
private final int id = counter++;
private final int priority;
protected static List<PrioritizedTask> sequence = new ArrayList<>();
public PrioritizedTask(int priority) {
this.priority = priority; //确定优先级
sequence.add(this); //将任务添加到列表中
}
@Override
public int compareTo(PrioritizedTask o) {
return priority < o.priority ? 1 : (priority > o.priority ? -1 : 0); // 定义优先级计算方式
}
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(random.nextInt(250));
} catch (InterruptedException e) {}
System.out.println(this);
}
@Override
public String toString() {
return String.format("[%1$-3d]", priority) + " Task " + id;
}
public String summary() {
return "(" + id + ": " + priority + ")";
}
//PrioritizedTask的内部类
public static class EndSentinel extends PrioritizedTask {
private ExecutorService exec;
public EndSentinel(ExecutorService exec) {
super(-1); //调用父类构造函数确定线程池优先级
this.exec = exec;
}
@Override
public void run() {
int count = 0;
for (PrioritizedTask pt : sequence) {
System.out.print(pt.summary());
if (++count % 5 == 0) {
System.out.println();
}
}
System.out.println();
System.out.println(this + " Calling shutdownNow()");
exec.shutdownNow();
}
}
}
// 生产者
class PrioritizedTaskProducer implements Runnable {
private Random random = new Random(47);
private Queue<Runnable> queue;
private ExecutorService exec;
public PrioritizedTaskProducer(Queue<Runnable> queue, ExecutorService exec) {
this.queue = queue;
this.exec = exec;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
queue.add(new PrioritizedTask(random.nextInt(10))); // 往PriorityBlockingQueue中添加随机优先级的任务
Thread.yield();
}
try {
for (int i = 0; i < 10; i++) {
TimeUnit.MILLISECONDS.sleep(250);
queue.add(new PrioritizedTask(10)); // 往PriorityBlockingQueue中添加优先级为10的任务
}
for (int i = 0; i < 10; i++) {
queue.add(new PrioritizedTask(i));// 往PriorityBlockingQueue中添加优先级为1-10的任务
}
queue.add(new PrioritizedTask.EndSentinel(exec));
} catch (InterruptedException e) {}
System.out.println("Finished PrioritizedTaskProducer");
}
}
//消费者
class PrioritizedTaskConsumer implements Runnable {
private PriorityBlockingQueue<Runnable> queue;
public PrioritizedTaskConsumer(PriorityBlockingQueue<Runnable> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
queue.take().run(); // 任务的消费者,从PriorityBlockingQueue中取出任务执行
}
} catch (InterruptedException e) {}
System.out.println("Finished PrioritizedTaskConsumer");
}
}
|
运行结果

5.延时队列
DelayQueue
DelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。队列中的元素必须实现Delayed接口和Comparable接口,也就是说DelayQueue里面的元素必须有public int compareTo(To)和long getDelay(TimeUtil unit)方法存在,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。可以将DelayQueue运用在一下场景中:
.缓存系统的设计。可以用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。
.定时任务调度。使用DelayQueue保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行,比如TimerQueue就是使用DelayQueue实现的
看一下DelayQueue的源码,来理解一下DelayQueue的实现原理和机制:
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 | public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
private final transient ReentrantLock lock = new ReentrantLock();
private final PriorityQueue<E> q = new PriorityQueue<E>();
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);//获取延时
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
...
}
|
看一下DelayQueue的使用实例
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 | import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Student implements Delayed{
private String name;
private long submitTime;
private long workTime;
public String getName(){
return this.name+"交卷,用时"+workTime;
}
public Student(String name,long submitTime){
this.name=name;
this.workTime=submitTime;
this.submitTime=TimeUnit.NANOSECONDS.convert(submitTime,TimeUnit.MILLISECONDS)+System.nanoTime();
System.out.println(this.name+"交卷,用时"+workTime);
}
public long getDelay(TimeUnit unit){
return unit.convert(submitTime-System.nanoTime(),unit.NANOSECONDS);
}
public int compareTo(Delayed o){
Student that=(Student)o;
return submitTime>that.submitTime?1:(submitTime<that.submitTime? -1:0);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import java.util.concurrent.DelayQueue;
public class DelayQueueTest {
public static void main(String[] args) throws Exception{
final DelayQueue<Student> bq=new DelayQueue<Student>();
for (int i=0;i<5;i++) {
Student student=new Student("学生"+i,Math.round((Math.random()*10+i)));
bq.put(student);
}
System.out.println("bq.peek()"+bq.peek().getName());
for (int i=0;i<5;i++) {
System.out.println("bq.take()"+bq.take().getName());
}
}
}
|

6.同步队列SynchronousQueue
SynchronousQueue是一个不存储元素的阻塞队列,每一个put操作必须等待一个take操作, 否则不能继续添加元素。SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数值直接传递给消费者线程。队列本身并不存储任何元素,非常适合传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue.
来看一下SynchronousQueue的源码,理解一下SynchronousQueue的实现原理和机制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | public class SynchronousQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
abstract static class Transferer<E> {
abstract E transfer(E e, boolean timed, long nanos);
}
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();//栈(后进先出)与队列(先进先出),实现非公平性与公平性的关键
}
public E take() throws InterruptedException {
E e = transferer.transfer(null, false, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
...
}
|
声明一个SynchronousQueue有两种不同的方式,它们之间有着不太一样的行为。公平模式和非公平模式的区别:如果采用公平模式,SynchronousQueue会采用公平锁,并配合一个FIFO队列来阻塞多余的生产者和消费者,从而体系整体的公平策略;但如果是非公平模式(SynchronousQueue默认),SynchronousQueue采用非公平锁,同时配合一个LIFO队列来管理多余的生产者和消费者,而后一种模式,则很容易出现饥渴的情况,即可能有某些生产者或者是消费者的数据永远都得不到处理。
因为SynchronousQueue没有内部容量,所以只提供一下方法:
-
-
| Modifier and Type | Method and Description |
|---|
void | clear() Does nothing. 不做任何 |
boolean | contains(Object o) Always returns false. 总是返回false |
boolean | containsAll(Collection<?> c) Returns false unless the given collection is empty. 除非给定的集合是空的才返回false |
int | drainTo(Collection<? super E> c) Removes all available elements from this queue and adds them to the given collection. 从队列中移除所有可用的元素并将它们添加到给定集合 |
int | drainTo(Collection<? super E> c, int maxElements) Removes at most the given number of available elements from this queue and adds them to the given collection. 从队列中移除给定数量的元素并将它们添加到给定集合 |
boolean | isEmpty() Always returns true. 总是返回true |
Iterator<E> | iterator() Returns an empty iterator in which hasNext always returns false. 返回空迭代器,hasNext()方法总是返回false |
boolean | offer(E e) Inserts the specified element into this queue, if another thread is waiting to receive it. 插入指定元素到队列中如果另一个线程整在等待获取它 |
boolean | offer(E e, long timeout, TimeUnit unit) Inserts the specified element into this queue, waiting if necessary up to the specified wait time for another thread to receive it. 插入指定元素到队列中,在给定时间内等待直到另一个线程获取它 |
E | peek() Always returns null. 总是返回null |
E | poll() Retrieves and removes the head of this queue, if another thread is currently making an element available. 如果当前有另一个线程,则检索并删除此队列的头 |
E | poll(long timeout, TimeUnit unit) Retrieves and removes the head of this queue, waiting if necessary up to the specified wait time, for another thread to insert it. 检索并删除此队列的头,如有必要,等待指定的等待时间,等待另一个线程插入它。 |
void | put(E e) Adds the specified element to this queue, waiting if necessary for another thread to receive it. 添加指定的元素到队列,必要时等待另外一个线程来接受它 |
int | remainingCapacity() Always returns zero. 总是返回0 |
boolean | remove(Object o) Always returns false. 总是返回false |
boolean | removeAll(Collection<?> c) Always returns false. 总是返回false |
boolean | retainAll(Collection<?> c) Always returns false. 总是返回false |
int | size() Always returns zero. 总是返回0 |
Spliterator<E> | spliterator() Returns an empty spliterator in which calls to Spliterator.trySplit() always return null. 返回空的分割器,该分割器调用 Spliterator.trySplit()方法总是返回null |
E | take() Retrieves and removes the head of this queue, waiting if necessary for another thread to insert it. 检索并移除队列的头元素,如果队列为空,阻塞直到另一个线程插入元素 |
Object[] | toArray() Returns a zero-length array. 返回长度为0的数组 |
<T> T[] | toArray(T[] a) Sets the zeroeth element of the specified array to null (if the array has non-zero length) and returns it. |
简单使用示例
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 | import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
public class SynchronousQueueTest {
public static void main(String[] args){
System.out.println("begin:"+(System.currentTimeMillis()/1000));
//定义一个SynchronousQueue
final SynchronousQueue<String> sq=new SynchronousQueue<String>();
//定义一个数量为1的信号量,其作用相当于一个互斥锁
final Semaphore sem=new Semaphore(1);
for(int i=0;i<10;i++){
new Thread(new Runnable(){
public void run(){
try{
sem.acquire();//获取
String input=sq.take();//取出队列里面的元素
String output=TestDo.doSome(input);
System.out.println(Thread.currentThread().getName()+":"+output);
sem.release();//释放
}catch(InterruptedException e){
e.printStackTrace();
}
}
}).start();
}
for(int i=0;i<10;i++){
String input=i+"";
try{
sq.put(input);//插入到队列中
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class TestDo{
public static String doSome(String input){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
String output=input+":"+(System.currentTimeMillis()/1000);
return output;
}
}
|
运行结果

7.链表双向阻塞队列LinkedBlockingDeque
LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列。所谓双向队列指的是你可以从队列的两端插入和移出元素。双端队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。相比其他的阻塞队列,LinkedBlockingDeque多了addFirst,addLast,offerFirst,offerLast,peekFirst,peekLast等方法,以First单词结尾的方法,表示插入,获取(peek)或移除双端队列的第一个元素,以Last单词结尾的方法,表示插入,获取或移除双端队列的最后一个元素。另外插入方法add等同于addLast,移除方法remove等效于removeFirst。
在初始化LinkedBlockingDeque是可以设置容量防止其过度膨胀,另外,双向阻塞队列可以运用在“工作窃取”模式中,有点和LinkedBlockingQueue类似,这里就不多说了。
看一下LinkedBlockingDeque的源码
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 | public class LinkedBlockingDeque<E>
extends AbstractQueue<E>
implements BlockingDeque<E>, java.io.Serializable {
transient Node<E> first;
transient Node<E> last;
private transient int count;
private final int capacity;
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
++count;
notEmpty.signal();
return true;
}
/**
* Links node as last element, or returns false if full.
*/
private boolean linkLast(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)
return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
++count;
notEmpty.signal();
return true;
}
...
}
|
简单示例如下
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 | import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
public class TestB {
public static void main(String[] args) throws Exception {
Random randon=new Random(100);
LinkedBlockingDeque<Integer> bq=new LinkedBlockingDeque<>();
for (int i=0;i<10;i++) {
new Thread(()->{
try{
bq.put(randon.nextInt(100));//添加到队尾
}catch(InterruptedException e){
}
}).start();
}
for(int i=0;i<5;i++){
new Thread(()->{
try{
System.out.println(Thread.currentThread().getName()+":"+bq.take());//从队头开始获取
}catch(InterruptedException e){
}
}).start();
}
}
}
|
8.链表传输队列LinkedTransferQueue
LinkedTransferQueue是一个由链表结构组成的无界传输队列
TransferQueue是一个继承了BlockingQueue的接口,并且增加若干新的方法。LinkedTransferQueue是实现类,其定义为一个无界的队列,一样具有先进先出(FIFO:first-in-first-out)的特性。Doug Lea这样评价它:TransferQueue是一个聪明的队列,它是ConcurrentLinkedQueue,SynchronousQueue(在公平模式下),无界的 LinkedBlockingQueues等的超集。显然易见,混合了若干高级特性,并且具有高性能的一个组合体,一个多面手。单纯从队列来看,TransferQueue接口增加了一些很实用的新特性。
transfer算法比较复杂,实现很难看明白。大致的理解是采用所谓双重数据结构。之所以叫双重,其原因是方法都是通过两个步骤完成:保留与完成。比如,消费者线程从一个队列中取元素,发现队列为空,它就生成一个空元素放入队列,所谓空元素就是元素项字段为空。然后消费者线程在这个字段上继续等待,这叫保留。直到一个生产者线程意欲向队列中放入一个元素,这里它发现最前面的元素的数据项字段为NULL,它就直接把自己数据填充到这个元素中,即完成了元素的传送。大体是这个意思,这种方式优美地完成了线程之间的高效协作。其transfer方法提供了线程之间交换对象的捷径的方法,如下所述:
-
-
void | transfer(E e) Transfers the element to a consumer, waiting if necessary to do so. 若当前存在一个正在等待的消费者线程,即立刻移交之;否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,直到有消费者线程取走该元素 |
boolean | tryTransfer(E e) Transfers the element to a waiting consumer immediately, if possible. 若当前存在一个正在等待获取的消费者进程(使用take()或者poll()函数),使用该方法会即刻转移\传输对象元素e;若不存在,则返回false,并且不进入队列。这是一个不阻塞的操作。 |
boolean | tryTransfer(E e, long timeout, TimeUnit unit) Transfers the element to a consumer if it is possible to do so before the timeout elapses. 若当前存在一个正在等待获取的消费者线程,会立即传输给它;否则将插入元素e到队列尾部,并且等待被消费者线程获取消费掉。若在指定的时间内元素e无法被消费者线程获取,则返回false,同时该元素移除。 |
-
-
int | size() Returns the number of elements in this queue. 因为队列的异步特性,检测当前队列的元素个数需要逐一迭代,可能会得到一个不太准确的结果,尤其是在遍历时有可能队列发生更改。 |
批量操作类似于addAll,removeAll,retainAll,containsAll,equals,toArray等方法,API不能保证一定会立即执行。因此,我们在使用过程,不能有所期待,这是一个具有异步特性的队列。
注意事项:
1。无论是transfer还是tryTransfer方法,在>=1个消费者线程等待获取元素时(此时队列为空),都会立刻转交,这属于线程之间的元素交换。注意,这时元素并没有进入队列。
2.在队列中已有数据情况下,transfer将需要等待前面数据被消费掉,直到传递的元素e被消费者线程取走为止。
3.使用transfer方法,工作者线程可能会被阻塞到生产的元素被消费掉为止。消费者进程等待为零的情况下,各自的处理元素入队与否情况有所不同。
4.size()方法,需要迭代,可能不太准确,尽量不要调用。
来看部分LinkedTransferQueue的源码,理解一下LinkedTransferQueue的实现原理和机制:
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 | public class LinkedTransferQueue<E> extends AbstractQueue<E>
implements TransferQueue<E>, java.io.Serializable {
public void transfer(E e) throws InterruptedException {
if (xfer(e, true, SYNC, 0) != null) {
Thread.interrupted(); // failure possible only due to interrupt
throw new InterruptedException();
}
}
private E xfer(E e, boolean haveData, int how, long nanos) {
if (haveData && (e == null))
throw new NullPointerException();
Node s = null; // the node to append, if needed
retry:
for (;;) { // restart on append race
for (Node h = head, p = h; p != null;) { // find & match first node
boolean isData = p.isData;
Object item = p.item;
if (item != p && (item != null) == isData) { // unmatched
if (isData == haveData) // can't match
break;
if (p.casItem(item, e)) { // match
for (Node q = p; q != h;) {
Node n = q.next; // update by 2 unless singleton
if (head == h && casHead(h, n == null ? q : n)) {
h.forgetNext();
break;
} // advance and retry
if ((h = head) == null ||
(q = h.next) == null || !q.isMatched())
break; // unless slack < 2
}
LockSupport.unpark(p.waiter);
return LinkedTransferQueue.<E>cast(item);
}
}
Node n = p.next;
p = (p != n) ? n : (h = head); // Use head if p offlist
}
if (how != NOW) { // No matches available
if (s == null)
s = new Node(e, haveData);
Node pred = tryAppend(s, haveData);
if (pred == null)
continue retry; // lost race vs opposite mode
if (how != ASYNC)
return awaitMatch(s, pred, e, (how == TIMED), nanos);
}
return e; // not waiting
}
}
public boolean tryTransfer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)
return true;
if (!Thread.interrupted())
return false;
throw new InterruptedException();
}
public E take() throws InterruptedException {
E e = xfer(null, false, SYNC, 0);
if (e != null)
return e;
Thread.interrupted();
throw new InterruptedException();
}
...
}
|
简单使用例子
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 | import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class TestB {
public static void main(String[] args) throws Exception {
TransferQueue<String> queue=new LinkedTransferQueue<String>();
Thread producer=new Thread(()->{
try{
while(true){
if(queue.hasWaitingConsumer()){
queue.transfer("your lucky number"+(new Random().nextInt(100)));
}
TimeUnit.SECONDS.sleep(1);
}
}catch(InterruptedException e){}
});
producer.setDaemon(true);
producer.start();
for(int i=0;i<10;i++){
Thread consumer=new Thread(()->{
try{
System.out.println("Consumer"+Thread.currentThread().getName()+queue.take());
}catch(InterruptedException e){
}
});
consumer.setDaemon(true);
consumer.start();
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
|
运行结果

9.同步计数器CountDownLatch
CountDownLatch是一个同步辅助类,直译过来就是倒数器(CountDown)门闩(Latch)。倒计数不用说,,门闩的意思顾名思义就是阻止前进。在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。用给定的计数器初始化CountDiwnLatch。由于调用了countDown()方法,所以在当当前计数到达零之前,await方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只出现一次-计数无法被重置。
主要的方法有:
-
-
CountDownLatch(int count) Constructs a CountDownLatch initialized with the given count. 构造一个用给定计数初始化的CountDownLatch。 |
-
-
void | await() Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted. 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 |
boolean | await(long timeout, TimeUnit unit) Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted, or the specified waiting time elapses. 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间 |
void | countDown() Decrements the count of the latch, releasing all waiting threads if the count reaches zero. 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 |
long | getCount() Returns the current count. 返回当前计数 |
String | toString() Returns a string identifying this latch, as well as its state. |
使用场景:
在一些应用场合中,需要等待某个条件达到要求后才能做后面;同时当线程都完成后也会触发事件,以便进行后面的操作。这个时候就可以使用CountDownLatch.CountDownLatch最重要的方法是 countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。比如,下面两种实际应用场景:
应用场景1:开5个多线程取下载,当5个线程都执行完了才算下载成功!
应用场景2:当用户多文件上传的时候,可以采用多线程上传,当多个文件都上传成功的时候才算真正的上传成功。
举例:
模拟一个场景,只有三个程序都干完活了,才算项目完成。实例如下:
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 | import java.util.concurrent.CountDownLatch;
public class TestB {
public static void main(String[] args) throws Exception {
CountDownLatch latch=new CountDownLatch(3);
Worker worker1=new Worker("Jack 程序员1",latch);
Worker worker2=new Worker("Rose 程序员2",latch);
Worker worker3=new Worker("Json 程序员3",latch);
worker1.start();
worker2.start();
worker3.start();
latch.await();
System.out.println("Main thread end");
}
static class Worker extends Thread{
private String workerName;
private CountDownLatch latch;
public Worker(String workerName,CountDownLatch latch){
this.workerName=workerName;
this.latch=latch;
}
@Override
public void run(){
try{
System.out.println("Worker: "+workerName+"is begin.");
Thread.sleep(1000L);
System.out.println("Worker: "+workerName+"is end.");
}catch(InterruptedException e){
e.printStackTrace();
}
latch.countDown();
}
}
} |
运行结果 注意System.out.println("Main thread end");最后执行,将latch.await();去掉后结果就不同了

看一下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 | public class CountDownLatch {
/**
* 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();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected 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;
}
}
}
private final Sync sync;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
public void countDown() {
sync.releaseShared(1);
}
...
}
|
其实·我们看到CountDownLatch源码相对比较简单,主要是利用AbstractQueueSynchronizer来实现。而AbstractQueuedSynchronizer其实不难发现,我们如果去查看一下ReentrantLock,CountDownLatch,Semaphore,FutureTask,ThreadPoolExecutor的源码的话,都会发现有个名叫Sync的静态内部类,继承自AbstractQueuedSynchronizer。AbstractQueuedSynchronizer是java.util.concurrent的核心组件之一,它为并发包中的其他synchronizers提供了一组公共的基础设施。
10.抽象队列同步器AbstractQueuedSynchronizer
AbstractQueuedSynchronizer是java.util.concurrent的核心组件之一,它提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架。该类利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础。使用的方法是继承,子类通过继承同步器并需要实现它的方法来管理其状态,管理的方式就是通过类似acquire和release的方式来操纵状态。然而,多线程环境中对状态的操纵必须确保原子性,因此子类对于状态的把握,需要使用这个异步器提供的以下三个方法对状态进行操作:
。AbstractQueuedSynchronizer.getState()
.AbstractQueuedSynchronizer.setState(int)
.AbstraQueuedSynchronizer.compareAndSetState(int,int)
子类推荐被定义为自定义同步装置的内部类,就像CountDownLatch里面一样,同步器自身没有实现任何同步接口,它仅仅是为定义了若干acquire之类的方法提供使用。该同步器既可以作为排他模式也可以作为共享模式,当它被定义为一个排他模式时,其他线程对它的获取就被阻止,而共享模式对于多个线程获取都可以成功。




对概念有了基本的理解之后我们来看一下官方的demo,就可以实现一个简单的自定义锁了。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | public class MyLockDemo implements Lock{
private static class Sync extends AbstractQueuedSynchronizer{
//是否处于占用状态
protected boolean isHeldExclusively(){
return getState()==1;
}
//当状态为0的时候获取锁
public boolean tryScquire(int acquires){
assert acquires==1;
if(compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
...
}
}
|
最后,AbstractQueuedSynchronizer子类,一般都是在内部类里面使用的,不同的同步机制和阻塞机制,释放锁和加锁的逻辑可能都不太一样,AbstractQueuedSynchronizer只是提供了一种基于FIFO队列的,可以用于构建锁或者其他相关同步装置的基础框架,我i们可以结合上面的CountDownLatch和下面讲到的Semaphote再做下理解。
11.同步计数器Semaphore
Semaphore是一个计数信号量。从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取许可。每个release()添加一个许可,从而可能释放一个正在阻塞的读取者。就像排队进入上海博物馆一样,先放几个人进去,等着几个人走了,然后再放几个人进入,就像是一种排队机制。
构造方法
-
-
Semaphore(int permits) Creates a Semaphore with the given number of permits and nonfair fairness setting. 创建具有给定的许可数和默认的非公平的公平设置的Semaphore数量 |
Semaphore(int permits, boolean fair) Creates a Semaphore with the given number of permits and the given fairness setting. 创建具有给定的许可数和给定的公平设置的Semaphore数量 |
常用方法
-
-
| Modifier and Type | Method and Description |
|---|
void | acquire() Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted. 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,除非线程被中断。 |
-
-
int | availablePermits() Returns the current number of permits available in this semaphore. 返回此信号量当前可用的许可数 |
-
-
int | drainPermits() Acquires and returns all permits that are immediately available. 获取并返回立即可用的所有许可 |
-
-
int | getQueueLength() Returns an estimate of the number of threads waiting to acquire. 返回正在等待线程的估计数目 |
-
-
boolean | hasQueuedThreads() Queries whether any threads are waiting to acquire. 查询是否有线程正在等待获取 |
-
-
boolean | isFair() Returns true if this semaphore has fairness set true. 如果此信号量的公平设置为true,则返回true |
-
-
protected void | reducePermits(int reduction) Shrinks the number of available permits by the indicated reduction. 根据指定的缩减量减小可用的许可的数量。 |
-
-
void | release() Releases a permit, returning it to the semaphore. 释放一个许可,将其返回给信号量 |
void | release(int permits) Releases the given number of permits, returning them to the semaphore. 释放给定的许可数,将其返回给信号量 |
-
-
boolean | tryAcquire() Acquires a permit from this semaphore, only if one is available at the time of invocation. 仅在调用此信号量存在一个可用许可,才从信号量获取许可 |
使用场景:
排队场景,资源有限的房间,资源有限的群等等。常见的实际应用场景包括线程池,连接池等
实例:
假设一个服务器资源有限,只允许同时3个人进行访问,一共来了10个人的场景。
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 | import java.util.concurrent.Semaphore;
public class TestB {
public static void main(String[] args) throws Exception {
final Semaphore semaphore=new Semaphore(3);
for(int i=0;i<10;i++){
final int no=i;
Runnable thread=new Runnable(){
public void run(){
try{
System.out.println("用户"+no+"连接上了");
Thread.sleep(300L);
semaphore.acquire();
System.out.println("用户"+no+"开始访问后台程序...");
Thread.sleep(1000L);
semaphore.release();
System.out.println("用户"+no+"访问结束");
}catch(InterruptedException e){
e.printStackTrace();
}
}
};
new Thread(thread).start();
}
System.out.println("Main thread end!");
}
}
|
运行结果

从结果上可以看的出来,10个人同时进来,但是只能同时3个人访问资源,释放一个允许进来一个
Semaphore的实现原理
来看一下Semaphore的部分源码体会一下
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 | public class Semaphore implements java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* Fair version
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
...
}
|
12.同步计数器CyclicBarrier
CyclicBarrier是一个同步辅助类,翻译过来叫循环栅栏,循环屏障。它允许一组线程互相等待,直到到达某个公共屏障点,然后所有的这组线程再同步往后面执行。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。不要错误理解了,不是线程屏障可以重复使用,而是多个线程都像多个循环一样,都循环到这个点了,再一起开始往后面执行。
构造函数
-
-
| Constructor and Description |
|---|
CyclicBarrier(int parties) Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and does not perform a predefined action when the barrier is tripped. 创建一个新的CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动barrier时执行预定义的操作。 |
CyclicBarrier(int parties, Runnable barrierAction) Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and which will execute the given barrier action when the barrier is tripped, performed by the last thread entering the barrier. 创建一个新的CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动barrier时执行给定的屏障操作,该操作由最后一个进入barrier的线程执行。 |
主要的方法有
-
-
| Modifier and Type | Method and Description |
|---|
int | await() Waits until all parties have invoked await on this barrier. 在所有参与者都已经在此barrier上调用await方法之前,将一直等待。 |
int | await(long timeout, TimeUnit unit) Waits until all parties have invoked await on this barrier, or the specified waiting time elapses. 在所有参与者都已经在此barrier上调用await方法之前,直到超出了指定的等待时间 |
int | getNumberWaiting() Returns the number of parties currently waiting at the barrier. 返回当前在屏障处等待的1参与者数目。 |
int | getParties() Returns the number of parties required to trip this barrier. 返回要求启动此barrier的参与者数目。 |
boolean | isBroken() Queries if this barrier is in a broken state. 查询此屏障是否处于损坏状态。 |
void | reset() Resets the barrier to its initial state. 将屏障重置为初始状态 |
使用场景:
大数据运算需要拆分成多步骤的时候。比如这么一个实际应用场景:我们需要统计全国的业务数据,其中各省的数据库是独立的,也就是说按省分库,并且统计的数据量很大,统计过程也比较慢。为了提高性能,快速计算,我们采取并发的方式,多个线程同时计算各省数据,每个省下面又用多线程,最后再汇总统计。
实例
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 | import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class TestB {
public static void main(String[] args) throws Exception {
CyclicBarrier barrier=new CyclicBarrier(3,new TotalTask());
BillTask worker1=new BillTask("111",barrier);
BillTask worker2=new BillTask("222",barrier);
BillTask worker3=new BillTask("333",barrier);
worker1.start();
worker2.start();
worker3.start();
System.out.println("Main thread end!");
}
static class TotalTask extends Thread{
public void run(){
System.out.println("所有子任务都执行完了,就开始执行主任务了");
}
}
static class BillTask extends Thread{
private String billName;
private CyclicBarrier barrier;
public BillTask(String workerName,CyclicBarrier barrier){
this.billName=workerName;
this.barrier=barrier;
}
public void run(){
try{
System.out.println("市区:"+billName+"运算开始:");
Thread.sleep(1000L);
System.out.println("市区:"+billName+"运算完成,等待中...");
barrier.await();
System.out.println("全部都结束,市区"+billName+"才开始后面的工作。");
}catch(InterruptedException|BrokenBarrierException e){
e.printStackTrace();
}
}
}
}
|
运行结果

从运行结果上面仔细体会与CountDownLatch的区别。请滑到前面比对一下源码与运行结果
。CountDownLatch:一个线程(或者多个),等待另外N个线程(用到CountDownLatch实例)全部完成某个事情之后才能执行。
。CyclicBarrier:N个线程相互等待,任何一个线程完成之前,剩余的已完成的N个进程之内的进程必须等待。
这样比对一下应该就清楚了,对于CountDownLatch来说,重点是那个“一个线程”,是它在等待,而另外那N个线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说。重点是那N个线程,它们之间任何一个没有完成,剩余的已完成的N个进程之内的进程必须等待。
CyclicBarrier的实现原理是,利用ReentrantLock做线程安全锁,实现线程安全等待。
来看一下CyclicBarrier的部分源码体会一下。
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 | public class CyclicBarrier {
private static class Generation {
boolean broken = false;
}
/** The lock for guarding barrier entry */
private final ReentrantLock lock = new ReentrantLock();
/** Condition to wait on until tripped */
private final Condition trip = lock.newCondition();
/** The number of parties */
private final int parties;
/* The command to run when tripped */
private final Runnable barrierCommand;
/** The current generation */
private Generation generation = new Generation();
*/
private int count;
private void nextGeneration() {
trip.signalAll();
count = parties;
generation = new Generation();
}
private void breakBarrier() {
generation.broken = true;
count = parties;
trip.signalAll();
}
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
if (!timed)
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
...
}
|