Source code interpretation--LinkedBlockingDeque explanation

   In the previous thread pool explanation, if the size of the thread is larger than the size of the core thread, it will be placed in the buffer queue, which is LinkedBlockingDeque. Let's take a closer look at this queue.

1. Definition of LinkedBlockingDequ

First look at the map


It's a queue, and it's blocking, and it tells you that it's thread-safe. LinkedBlockingDeque uses a doubly linked list. Node's declaration is as follows:

static final class Node< E > { E item ; // The current value Node< E > prev ; // Point to the predecessor node Node< E > next ; // Point to the successor node Node ( E x) { 
         item = x ;
 }
    
    
    
        
}

Use two conditions to achieve blocking

//The condition of taking the value from the queue is judged. If the queue is empty, if there is a thread to take the value again, the thread will be blocked until the value is added, and the value can be taken away by others
 private final Condition notEmpty = lock .newCondition() ;
 //Add a value to the queue, the queue is full and needs to be blocked, wait until other threads take the value in the queue before adding the value
 private final Condition notFull = lock .newCondition() ;

This kind of congestion is a typical producer-consumer model. For example, if you go to listen to an old professor's lecture, there are only so many seats in the classroom. When the seats are full, the students in the room need to stand. If there are students sitting When you leave your seat, the next student can sit next to you.

The exclusive lock ReentrantLock used by the LinkedBlockingDeque lock will not be discussed in detail. Knowing that he is exclusive, other threads can wait.

2. Appearance of LinkedBlockingDeque

2.1 Initialization

public LinkedBlockingDeque () { //Unbounded? Unspecified maximum is the maximum value of int
     this ( Integer.MAX_VALUE ) ;
 }
public LinkedBlockingDeque ( int capacity) { //Specify the size of the queue, the advantage is to prevent over-expansion
     if (capacity <= 0 ) throw new IllegalArgumentException() ;
     this . capacity = capacity ;
 }
public LinkedBlockingDeque(Collection<? extends E> c) {
    this(Integer.MAX_VALUE);
    final ReentrantLock lock = this.lock;
    lock.lock(); //获取到锁
try {
        for (E e : c) {
            if (e == null)
                throw new NullPointerException();
            if (!linkLast(new Node<E    >(e))) //Add the values ​​in the collection one by one, if the queue is full, it will throw a Deque full exception
                 throw new IllegalStateException( "Deque full" ) ;
         }
    } finally {
        lock.unlock();
    }
}

2.2 Add queue value

  

public void put(E e) throws InterruptedException {
    putLast(e) ;
}

public void putLast ( E e) throws InterruptedException {
     if (e == null ) throw new NullPointerException() ; //If the added data is empty, throw a null pointer exception
     Node< E > node = new Node< E >(e ) ; //initial data node
     final ReentrantLock lock = this .lock ; lock.lock() ;
 //lock
     try {
         while (!linkLast(node)) //put the node to the end of the queue
             notFull .await() ; / block if the queue is full } finally    
    {
        lock.unlock();
    }
}
private boolean linkLast(Node<E> node) {
    // assert lock.isHeldByCurrentThread();
    if (count >= capacity)  //超出了容量,需要阻塞
        return false;
    Node<E> l = last;  //记录last节点
    node.prev = l;  //当前node的前驱指向last
    last = node;  last指向node
    if (first == null) //如果第一个为空,node就是第一个
        first = node;
    else
        l.next = node;  //最前一个last的next指针指向现在的node
    ++ count ; //Add 1 to the value in the queue
 notEmpty .signal () ; //Release resources, other threads can add values
     ​​return true;
 }    
2.3 Get queue value

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)  //获取队列的值,如果第一个值为null,需要等其他线程把值添加进去,它才能获取到值
            notEmpty.await();
        return x;
    } finally {
        lock.unlock();
    }
}
private E unlinkFirst() {  //出队列,移除第一个值
    // assert lock.isHeldByCurrentThread();
    Node<E> f = first;
    if (f == null)  //如果第一个值为空
        return null;
    Node<E> n = f.next; //n 指向第一个节点的下一个
    E item = f.item;  //拿到第一个节点里面的值
    f.item = null; //将第一个节点里面的值赋值为空,方便gc
    f.next = f; //之前第一个节点现在后继节点已经没有了,指向了自身,没有别的节点有对它的引用,会被gc线程清除
    first = n;  //现在第一个节点指向了之前第二个节点n
    if (n == null)  //如果第二个节点是null
        last = null;  //尾指针指向null
    else
        n.prev = null;  //否则的话,之前第二个的节点的前驱指针指向null
    --count;  //队列的数据减一
    notFull.signal();  //之前获取数据没有获取成功的线程现在可以拿取数据了
    return item;
}

三.总结

看完了LinkedBlockingDeque的源码实现是不是感觉很简单。它首先是一个双向链表,可以支持先进先出。其次线程安全,用的是ReentranLock。接着是阻塞式的,用的是notFull,notEmptyl两个Condition实现。

纸上得来终觉浅,绝知此事要躬行。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324607734&siteId=291194637