简介:
前面几篇文章分析了java.util包下面的一些常见容器,util下面的容器都是线程不安全的,所以我们开始看
java.util.concurrent下面的并发容器,从常见的LinkedBlockingQueue开始吧。
UML图:
属性:
构造方法:
虽然有3种,其实都一样,赋个初始容量,或者遍历某个queue,一个个新增进新queue。只不过需要注意一下head是null。
接下来看常用方法吧。
offer(E e):
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队列里面的线程
notFull.signal();
}
} finally {
//记得释放锁
putLock.unlock();
}
if (c == 0)
//c为0,代表队列有一个值了,所以去唤醒notEmpty队列里面的线程
signalNotEmpty();
return c >= 0;
}
关于ReentrantLock 跟Condition的部分可以看博主前两篇文章,已经分析过了。
enqueue(Node node):
private void enqueue(Node<E> node) {
//先赋尾节点的next,再赋新的尾节点
last = last.next = node;
}
add(E e):
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
跟offer一样
put(E e):
public void put(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
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();
}
仔细看,其实就是多 了一句notFull.await(),容量满了的话就等待。
poll():
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;
}
就是取值,比较简单。
take():
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
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
//满了就去通知notFull
signalNotFull();
return x;
}
跟poll()差不多,只不过多了一个wait。
dequeue():
private E dequeue() {
Node<E> h = head;
//取head的next,这才是真正的第一个节点,head是空节点
Node<E> first = h.next;
//把h的next指向自己,其实就是把h及后续的next的item都置为null
h.next = h;
//把first赋给head,此时的head相当于真正的第一个节点,item是有值的
head = first;
E x = first.item;
//这里把first的item置为null,因为head是空节点,item一定要是null
first.item = null;
return x;
}
LinkedBlockingQueue也没啥好说的,比较简单,精华都在ReentrantLock里面的。下篇分析延迟队列DelayQueue。