java 并发编程系列文章目录
1 LinkedBlockingDeque是什么
首先queue是一种数据结构,一个集合中,先进后出,有两种实现的方式,数组和链表。从尾部追加,从头部获取。Deque是两端都可以添加,且两端都可以获取,所以它的方法会有一系列的Last,Frist语义,添加或获取等操作会指明哪个方向的,这也是Deque接口的定义。
那如果你不指定语义 如add()方法,他会默认调用addLast
综上所述,LinkedBlockingDeque是一个线程安全的双端阻塞队列。
2 核心属性详解
相对于LinkedBlockingQueue 他只能使用一把锁,不能分成put 和 take两把锁。因为此时双端都可以put 和 take,所以只能使用一个锁,通过锁,对其链表实现线程安全的操作。
//队列的头尾节点
transient Node<E> first;
transient Node<E> last;
//队列中元素的数量
private transient int count;
//指定的队列的容量,默认Int最大值
private final int capacity;
//实现线程安全的使用的锁
final ReentrantLock lock = new ReentrantLock();
//获取元素的时候如果空了会使用它让其自己等待
private final Condition notEmpty = lock.newCondition();
//添加元素的时候如果满了(count == capacity)会使用它让其自己等待
private final Condition notFull = lock.newCondition();
3 核心方法详解
下面会列举First系列的方法,因为last系列相对于first只是链表方向不一样,操作都是一致的。
3.1 addFirst(E e)
调用offerFirst 如果未成功 则抛出异常
public void addFirst(E e) {
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
}
3.2 offerFirst(E e)
在链表的头部添加一个元素,使用ReentrantLock 保证线程安全
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();
}
}
private boolean linkFirst(Node<E> node) {
//如果元素已经超出容量,返回添加失败
if (count >= capacity)
return false;
//链表的操作,用的是双向链表,first变成自己,之前的first是自己的next
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
//元素统计数量加1
++count;
//唤醒那些因为获取不到元素而阻塞的线程
notEmpty.signal();
return true;
}
3.3 putFirst(E e)
相对于offer一个元素 如果元素数量已到达容量上线,会阻塞住等待元素被取走才放入
在juc下面 put add take等语义都是一致的
public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
//获取锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
//添加失败就阻塞住等待唤醒
while (!linkFirst(node))
notFull.await();
} finally {
lock.unlock();
}
}
3.4 removeFirst()
从头结点移除一个元素,调用的是pollFirst,拿出元素返回,元素==null会抛出异常
public E removeFirst() {
E x = pollFirst();
if (x == null) throw new NoSuchElementException();
return x;
}
3.5 pollFirst()
取出first元素并返回,会返回null
public E pollFirst() {
//加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 取出first, 链表的操作和count的维护以及唤醒添加元素因为容量到达上线的等待的线程
return unlinkFirst();
} finally {
lock.unlock();
}
}
3.6 takeFirst()
获取一个first元素,区别poll 在于会阻塞等待
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();
}
}
3.7 其他
对于last系列方法,只是链表的操作方向不一样而已
其次默认的不带last 和 first系列的方法,即原始的add put等方法,可以等同LinkedBlockingQueue。
LinkedBlockingDeque内部是一个双向链表,支持了链表两端操作,所以方法不一一介绍,原理都是一样。文章来源:https://www.toymoban.com/news/detail-691951.html
4 总结
LinkedBlockingDeque使用双端队列,通过ReentrantLock保证线程安全,实现了双端的线程安全的阻塞队列。文章来源地址https://www.toymoban.com/news/detail-691951.html
到了这里,关于java并发编程 LinkedBlockingDeque详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!