分析了好多集合源码之后,我也在想我这么做的初衷是否改变过?答案是未曾改变,写自己喜欢的内容才可持续地走下去,不然,只会搞得自己疲惫不堪。当然了,能帮助到需要的人是再好不过的了。
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
//容量赋值操作
public LinkedBlockingDeque(int capacity) {
//参数校验,若小于0,直接抛出不合法的参数异常
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
public boolean add(E e) {
addLast(e);
return true;
}
//方法的复用操作
public void addLast(E e) {
if (!offerLast(e))
throw new IllegalStateException("Deque full");
}
//继续复用已有的方法
public boolean offerLast(E e) {
//首先,这个阻塞队列是不允许添加元素为null的情况的
//否则直接抛出NPE异常
if (e == null) throw new NullPointerException();
//构造一个数据为e的节点node
Node<E> node = new Node<E>(e);
//获取锁实例对象,由此可见这是一个线程安全的方法
final ReentrantLock lock = this.lock;
//进行加锁操作
lock.lock();
try {
//复用已有的方法,等下继续分析了
return linkLast(node);
} finally {
//进行解锁操作,一定要放在finally语句块里进行
lock.unlock();
}
}
// linkLast之所以单独抽取为一个方法是为了达到高度解耦
private boolean linkLast(Node<E> node) {
//如果队列里面的元素个数count已经大于队列的容量了
//说明队列已经满了,不允许再装入元素了
if (count >= capacity)
return false;
//将最后一个节点引用赋值给临时局部变量l
Node<E> l = last;
//由于node节点之间是根据地址指针指向连接在一起的,所以这里node.prev=l;
node.prev = l;
//最后将新节点node赋值给最后一个节点last
last = node;
//为啥要在这里判断first==null呢?思考一下?
//有可能第一次添加时,队列为空,所以此时node就是第一个节点了
if (first == null)
first = node;
else
//由于队列里面已经存在元素了,所以将node直接挂载在最后一个节点的后面
l.next = node;
//队列的元素个数增加一
++count;
//进行线程间的通信机制,如何线程通信,在之前的文章已提到,可以去看下
notEmpty.signal();
return true;
}
进行线程间的通信机制,如何线程通信,在之前的文章已提到,可以去看下这篇文章的最后。
public boolean offer(E e) {
return offerLast(e);
}
//复用已有的方法
public boolean offerLast(E e) {
//这个方法已经在add()方法分析过了,在此不再分析了哈
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkLast(node);
} finally {
lock.unlock();
}
}
offer()方法和add()方法都是在队列的尾部添加元素,其内部的实现都是一样的,这里你学习到方法的复用就可以了,这里就不过多分析了。我们接下来分析的都是如何向队列里放入元素。
public void putFirst(E e) throws InterruptedException {
//这个方法就是在队列的队首位置添加元素,但是不允许添加元素为null的情况
if (e == null) throw new NullPointerException();
//构建一个元素为e的节点node
Node<E> node = new Node<E>(e);
//进行加减锁操作的步骤,主要是为了确保线程安全的
final ReentrantLock lock = this.lock;
lock.lock();
try {
//这是一个阻塞性的方法,看到这里的await就已经明白了吧
while (!linkFirst(node))
notFull.await();
} finally {
lock.unlock();
}
}
//linkFirst操作
private boolean linkFirst(Node<E> node) {
if (count >= capacity)
return false;
//首先将队列的队首引用赋值给临时局部变量f
Node<E> f = first;
//由于新节点是需要放在队首位置的,所以node.next=f
node.next = f;
//新节点赋值给队首元素
first = node;
//如果last为null,说明队列里还没元素的,直接将node赋值给last
if (last == null)
last = node;
else
// f.prev=node操作是第二个元素的前一个指向就是node了
f.prev = node;
//队列里的元素个数加一
++count;
notEmpty.signal();
return true;
}
public boolean offerFirst(E e) {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
//进行加锁操作
lock.lock();
try {
//这个方法也是在队列的队首位置添加元素,上面的方法分析过了
return linkFirst(node);
} finally {
//进行解锁操作
lock.unlock();
}
}
public void addFirst(E e) {
//如果添加失败,则抛出队列已满的异常给调用者
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
}
public E getFirst() {
//只是获取了队列的队首元素,但是元素没有出队列
E x = peekFirst();
if (x == null) throw new NoSuchElementException();
return x;
}
//peekFirst()方法
public E peekFirst() {
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//队列里面没有元素时,获取时,直接返回null,否则返回元素item
return (first == null) ? null : first.item;
} finally {
//解锁操作
lock.unlock();
}
}
public E getLast() {
//获取队列的最后一个元素
E x = peekLast();
if (x == null) throw new NoSuchElementException();
return x;
}
//线程安全的方法
public E peekLast() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//获取队尾元素
return (last == null) ? null : last.item;
} finally {
lock.unlock();
}
}
public E remove() {
return removeFirst();
}
//移除队首元素
public E removeFirst() {
E x = pollFirst();
if (x == null) throw new NoSuchElementException();
return x;
}
//线程安全的方法
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
}
//unlinkFirst()方法
private E unlinkFirst() {
//获取队首元素的引用
Node<E> f = first;
//若f==null,则表示队列没有元素,直接返回null即可
if (f == null)
return null;
//取队列的第二个元素赋值给临时局部变量n
Node<E> n = f.next;
//获取队首元素item
E item = f.item;
//将item置为null,等待gc触发
f.item = null;
f.next = f; // help GC
//将队列的第二个元素赋值给队首元素first
first = n;
//n==null表示队列里没有元素了
if (n == null)
last = null;
else
//n此时为队首元素,队首元素的prev是为null
n.prev = null;
//队列元素个数减一操作
--count;
//线程通信
notFull.signal();
return item;
}
public E poll() {
return pollFirst();
}
// pollFirst()方法也是一个线程安全的方法
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//这个方法已经分析过了,这个poll()方法表示队列的元素出队列
return unlinkFirst();
} finally {
lock.unlock();
}
}
take()方法是一个阻塞的方法,也是获取队首的元素,队列里面就没有这个元素了
public E take() throws InterruptedException {
return takeFirst();
}
//为啥是线程安全的,想必都知道了吧
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
while ( (x = unlinkFirst()) == null)
//如果队列里面不存在元素,则进行等待
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
public E element() {
//获取队首元素,但是不出队列
return getFirst();
}
public E peek() {
//获取队首元素
return peekFirst();
}
public int remainingCapacity() {
//线程安全的方法
final ReentrantLock lock = this.lock;
//加锁
lock.lock();
try {
//容量减去已有的队列元素个数,就是队列的剩余容量了
return capacity - count;
} finally {
//解锁
lock.unlock();
}
}
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//返回成员变量count,即队列元素个数
return count;
} finally {
lock.unlock();
}
}
public boolean contains(Object o) {
//前置校验,若元素为null,则直接返回false
if (o == null) return false;
//获取锁实例对象
final ReentrantLock lock = this.lock;
//加锁操作
lock.lock();
try {
//循环遍历队列的每一个元素
for (Node<E> p = first; p != null; p = p.next)
//若元素相等,则表示存在,直接返回true
if (o.equals(p.item))
return true;
return false;
} finally {
//解锁操作
lock.unlock();
}
}
public void clear() { //线程安全
final ReentrantLock lock = this.lock;
lock.lock();
try {
//循环遍历队列的每一个元素,将队列的每一个元素置为null
//等待gc触发,回收不可达对象
for (Node<E> f = first; f != null; ) {
//元素置为null
f.item = null;
//队列的下一个元素赋值给临时局部变量n
Node<E> n = f.next;
//队列的队首元素的f.prev置为null
f.prev = null;
//队列的队首元素的next置为null
f.next = null;
//将队列的下一个元素变为队首元素,即f=n
f = n;
}
//当队列的每一个元素都处理完了,即first=last=null
first = last = null;
//队列元素个数置为0
count = 0;
notFull.signalAll();
} finally {
//解锁操作
lock.unlock();
}
}
关于push()方法和pop()方法这里都不分析了,都大同小异,理解其它方法就可以了。
public boolean removeFirstOccurrence(Object o) {
if (o == null) return false;
//线程安全
final ReentrantLock lock = this.lock;
lock.lock();
try {
//循环判断队列的每一个元素,找到元素o,然后进行删除
for (Node<E> p = first; p != null; p = p.next) {
if (o.equals(p.item)) {
unlink(p);
return true;
}
}
return false;
} finally {
//解锁操作
lock.unlock();
}
}
//unlink()方法
void unlink(Node<E> x) {
//找到待删除元素的前一个元素p
Node<E> p = x.prev;
//找到待删除元素的下一个元素n
Node<E> n = x.next;
//如果p==null表示待删除的元素是队首元素
if (p == null) {
unlinkFirst();
//如果n==null,表示待删除的元素是队尾元素
} else if (n == null) {
//继续看下unlinkLast()方法
unlinkLast();
} else {
//这个时候,就是由可能删除的是中间元素
//p.next=n和n.prev=p就是为了连接队列的每一个元素
p.next = n;
n.prev = p;
//触发gc
x.item = null;
//队列元素个数减一
--count;
notFull.signal();
}
}
//unlinkLast()方法
private E unlinkLast() {
//将队列的队尾赋值给临时局部变量l
Node<E> l = last;
if (l == null)
return null;
//将最后一个元素置为l
Node<E> p = l.prev;
E item = l.item;
l.item = null;
l.prev = l; // help GC
//队尾元素的前一个元素作为最后一个节点引用,即last=p
last = p;
//如果p==null说明队列没有元素了,即first==null
if (p == null)
first = null;
else
//队列里还有元素,但是队尾元素的next需要置为null,因为队尾元素的下一个指向next就是null
p.next = null;
//队列元素个数减一
--count;
notFull.signal();
return item;
}
本篇提到最多的就是线程安全的关键字了,如何理解线程安全是后面文章可能会写的内容,差不多这篇文章分析完了之后,集合的源码分析就结束了,后面看下自己是否还有喜欢的文章可以分析了。