LinkedBlockingQueue learning

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/ko0491/article/details/90903428

LinkedBlockingQueue

Blocking queue LinkedB exclusive lock to achieve! OckingQueue

LinkedB lockingQueue linked list is implemented using one-way, it also has two
Node, it is used to store the first and last nodes, and there is an initial value of 0 atom COUNT variable, used to record
the number of queue elements. Another example of two ReentrantLock, respectively, for controlling the primary elements and dequeue the
protonic wherein takeLock only one thread is used to control elements may be obtained from the head of the queue, other threads must
wait, putLock controlled simultaneously only there is a thread can acquire the lock, add an element to the tail of the queue, other threads will
have to wait. In addition, notEmpty and notFull conditions are variable, they have a condition internal queue used to store feed
thread is blocked when the team and the team, this is actually a producer consumer model.

transient Node head;

/**

  • Tail of linked list.
  • Invariant: last.next == null
    */
    private transient Node last;

/ ** Lock held by take, poll , etc * /
// When perform take, poll other operations required to acquire the lock
private final ReentrantLock takeLock = new ReentrantLock ( );

/ ** Wait queue for waiting takes * /
// If the queue is empty, dequeue operations performed (such as Take) thread is placed in the queue waiting condition
private final Condition notEmpty = takeLock.newCondition () ;

/ ** Lock Held by put, offer, etc * /
// perform put, for an appropriate time to acquire the lock offer other operations *
private final ReentrantLock putLock = new ReentrantLock ( );

/ ** Waiting for the puts the Wait Queue * /
// When the queue is full, into the team performing operations (such as put) the thread 71 is placed in this condition team] waits
private final Condition notFull = putLock.newCondition () ;

/ ** Current Number of Elements * /
// current number of queue elements
private final AtomicInteger count = new AtomicInteger ( );

When the calling thread executing on tak e eue example LinkedBlockingQu, needs to obtain other operations to poll
tak eLock lock to ensure that only one thread can operate head node list. In addition, as a condition variable
notEmpty conditions inside the maintenance of the queue using a lock state management mechanism takeLock, so in tune
calling thread prior to await and signal method notEmpty must first get to takeLock lock, otherwise they will throw
out Illega! MonitorStateException exception. notEmpty the internal queue maintains a condition, when the line
when the process acquires the lock takeLock notEmpty await method of calling thread is blocked, then
the thread will be placed inside the conditions notEmpty queue waits until thread calls notEmpty
the signal method.

You need to acquire the execution putLock put, offer examples of other operations on the e LinkedBlockingQu eu
lock to ensure that only one thread can operate the end node list. Also due to the condition variable
conditions within the notFull maintain the queue using the lock mechanism putLock of state management, so call
before the call notFull await the method and si gn al thread must first acquire the putLock lock, otherwise it will throw
Illega! MonitorStateEx c eption exception. notFull the internal condition maintains a queue when the thread is eligible
to take the time to lock putLock invoked await notFull, the calling thread is blocked, then the thread will
be placed in conditions notFull internal queues to wait until there is a thread calls notFull method of signal.

Constructors
public a LinkedBlockingQueue () {
the this (Integer.MAX_VALUE);
}
public a LinkedBlockingQueue (int Capacity) {
IF (Capacity <= 0) the throw an IllegalArgumentException new new ();
this.capacity = Capacity;
// initialize the first and last node, so that they point to the sentinel node
Last = = new new head the node (null);
}

//Node
static class Node {
E item;

/**
 * One of:
 * - the real successor Node
 * - this Node, meaning the successor is head.next
 * - null, meaning there is no successor (this is the last node)
 */
Node<E> next;

Node(E x) { item = x; }

}

offer-add operation is added

public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException(“Queue full”);
}

Boolean the offer public (E E) {
// (. 1) an empty element throws a null pointer exception
IF (E == null)
the throw a NullPointerException new new ();
// taken as the current queue is full will be dropped into the element, then returns to false
Final COUNT = this.count of AtomicInteger; // current capacity
// is not full
IF (count.get () == capacity)
return to false;
// (. 3) the new node is configured to acquire an exclusive lock putLock
int c -1 =;
the Node Node new new = the Node (E);
Final of ReentrantLock putLock = this.putLock;
putLock.lock ();
the try {
// (. 4), such as mining proceeds dissatisfied queue queue, and increments the element count
if (count. GET () <Capacity) {
// add a new element
enqueue (node);
number + 1 // increment the counter.
count.getAndIncrement = C ();
//. 5 determines if there is a new element into the queue out of free space, the wake-up condition of the queue inside notFull
// call the await operation notFull because of (for example, when the execution method and put the queue is full) and blocked a
// thread, because the queue is now empty so there can wake up in advance a team into the thread.
IF (C +. 1 <Capacity)
notFull.signal ();
}
} finally {
// release lock 6
// acquired putLock lock release, to be noted here, release the lock which must be done in the finally,
// because even t can block thrown up, finally it is to be executed. Further because the other call put the lock release operation
// as the blocked thread will have to acquire a lock that
putLock.unlock ();
}
//. 7
IF (C == 0)
// C == O described in executing code (6) releases the lock queue when there are at least one element, the queue
// there are elements signalNotEmpty operation is performed,
signalNotEmpty ();
//. 8
return C> = 0;
}

/**

  • Links node at end of queue.
  • @param node the node
    */
    private void enqueue(Node node) {
    // assert putLock.isHeldByCurrentThread();
    // assert last.next == null;
    last = last.next = node;
    }

signalNotEmpty

private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}

Effect of this method is to activate notEmpty conditions queue because invoked await notEmpty (the ratio
when empty, such as calling the take method and the queue) and blocked a thread, which also shows the calling condition variable
method before to obtain the corresponding locks.

putLock offer by using the method to ensure the locking element operating in the new queue tail atoms' properties. Further,
the former method is called a condition variable must remember to obtain a corresponding lock, and only operating the end node into the queue list when the team noted.

put 操作
public void put(E e) throws InterruptedException {
//( 1 )如果为空元素则抛出空指针异常
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 node = new Node(e);
//(2 )构建新节点,并获取独占 19\putLock
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
/*
* Note that count is used in wait guard even though it is
* not protected by lock. This works because count can
* only decrease at this point (all other puts are shut
By Lock OUT *), and WE (or some OTHER Waiting PUT) are
* Signaled IF IT Ever Changes from Capacity. Similarly
* for All OTHER OTHER uses of COUNT in the wait Guards.
* /
// (3) If the queue is full, the waiting to enter blocking
the while (count.get () == Capacity) {
notFull.await ();
}
// Add to the last element
// (4) into the queue and incrementing
the enqueue (Node);
// increment counter . 1
C = count.getAndIncrement ();
IF (C +. 1 <Capacity)
// dissatisfied, you can continue to add, wake
notFull.signal ();
} the finally {
// unlock
putLock.unlock ();
}
IF (C == 0)
// wakeup
signalNotEmpty ();
}

Use putLock.locklntenuptibly code (2) () takes an exclusive lock, compared to offer process acquires
an exclusive lock method This method can be interrupted. Specifically the current thread is in the process of acquiring the lock, if it is
his thread interrupt flag is set then the current thread throws IntenuptedException exception, so put in operation acquisition
process is in the lock can be interrupted

Code (3) to determine if the current queue is already full, await notFull is call () method of the thread into the current
condition of the queue notFull, after the current thread is blocked pending release putLock acquired lock. Since putLock lock
is released, so now other threads have the opportunity to acquire a lock putLock

Code (3) Why use a while loop in determining whether the queue is empty rather than an if statement? This is considered
to be the current thread spurious wakeup problem, that is, no other thread calls the method notFull singal when notFull.
The await () will automatically return under certain conditions. If the if statement false wakeup then executes the code (4) of the element
prime enqueue operation, and incrementing the counter, but this time the queue is already full, thereby resulting in the number of queue elements is greater than the queue
capacity is provided, leading to the program An error occurred. And when using a while loop, if notFull.await () is false awakened,
then loop again to check the current queue is already full, if it is then waiting again.

poll operation
obtained from the head of the queue and removes an element if the queue is empty null is returned, the process is not blocked
public E poll () {
// get the current capacity size
Final COUNT = this.count of AtomicInteger;
// ( l) the queue is empty or returns null
IF (count.get () == 0)
return null;
// (2) an exclusive lock
E X = null;
int C = -1;

final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
    //(3 )队列不空则出队并递减计数
    if (count.get() > 0) {
        //出队列
        x = dequeue();
        //减一,计数器
        c = count.getAndDecrement();
        if (c > 1)
        //4  唤醒 线程,告诉线程,当前队列不是空
            notEmpty.signal();
    }
} finally {
    //解锁 5
    takeLock.unlock();
}
//移除之后,有一个空从头再来
if (c == capacity)
//唤醒 6
    signalNotFull();
return x;

}

dequeue
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node h = head;
Node first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}

private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}

Code (1) determines if the current queue is empty, the process directly returns null.
After the code (2) acquire an exclusive lock takeLock, the current thread to acquire the lock, other threads calling the poll or
be blocked hangs take method.

Code (3) to determine if the current queue is not empty dequeue operation is performed, then the counter is decremented. It should be thought
to test, how to ensure the code is executed when the queue is not empty 3.1, and code execution will not be empty when it 3.2? After all, this is not the
atomic operations, the code will appear 3.1 to determine the queue is not empty, but 3.2 to execute code when the queue is empty it?
Then we look at the areas in which the code prior to 3.2 will modify the count to count execution. Due to the current thread has got
a takeLock lock, so call the poll or take other method thread can not modify the count will come to count the
party. In fact, at this time if it can modify the count went to count the place because other thread calls put and offer operational,
because the two operations do not need to get takeLock lock is acquired putLock lock, but put in operation and offer
internal count is increased count, the above mentioned situation does not arise. In fact, just look at what areas handed
reduce the count to the count value, the count is decremented count value only appears above said, the code execution queue when 3.1
is not empty, and code execution queue is empty when the situation 3.2. We look at the code, only in the poll, take or remove
local operation of the count value is decremented count, but these three methods will need to get to takeLock lock to operate,
and the current thread has acquired the takeLock lock so other threads have no chance in the current count down count 'case
value, the code looks like 3.1, 3.2 are not atomic, but they are thread-safe

Code (4) If the determination. l then the current thread after removing an element off the queue which queue is not empty
C c is the number of elements in the queue before deleting elements), then this time you can take activated because the call is blocked method to
notEmpty conditions queue inside a thread

Code (6) Description of the current thread to remove elements of the current team before the head of the queue is full, after removal of the head elements currently in the queue
have at least one free position, this time we can call s igna! NotFull activation method because the call is put resistance
condition queue stuffed notFull's a thread,

poll code logic is relatively simple, it is worth noting that only the operation of the queue head node when retrieving an element.

peek operation

Get head of the queue elements but does not remove it from inside the queue, if the queue is empty, returns null. This method is not
blocked
public PEEK E () {
// Get capacity size. 1
IF (count.get () == 0)
return null;
// 2 acquires the lock
Final takeLock of ReentrantLock = this.takeLock;
takeLock.lock () ;
the try {

    Node<E> first = head.next;
    //3 
    if (first == null)
        return null;
    else
    //4 
        return first.item;
} finally {
    // 5
    takeLock.unlock();
}

}

Code (3) where necessary to decide firs t
whether null, can not execute code directly (in. Normally execute the code.> Description queue is not empty, but
the code Cl) (2) is not atomic operations and, also the queue is not empty, the code (2) at the point of execution (1) Analyzing
the acquired possible front latch or other threads take a poll operation causes the queue to become empty. Is eligible for the current thread is then
taken after lock, direct execution code (4) (first.item) throws a null pointer exception.

Operation take - interruptible
obtain the current head of the queue and remove it from the queue elements inside. If the queue is empty blocks the current thread until the queue
is not empty then return element, if blocking is set when the interrupt flag other threads were blocked thread will be thrown
I n terruptedExceptio n abnormal returns

E Take public () throws InterruptedException {
E X;
int C = -1;
// get the current capacity
Final COUNT = this.count of AtomicInteger;
// acquiring the lock. 1
Final takeLock of ReentrantLock = this.takeLock;
takeLock.lockInterruptibly ();
the try {
// (2) blocking the current queue is empty pull
the while (count.get () == 0) {
notEmpty.await ();
}
// output queue (3) and counts dequeued
x = dequeue () ;
// Save calculator from a
C = count.getAndDecrement ();
//. 4
IF (C>. 1)
notEmpty.signal ();
} the finally {
// unlock. 5
takeLock.unlock ();
}
//. 6
IF ( Capacity == C)
signalNotFull ();
return X;
}

In the code (1), the current thread to acquire an exclusive lock, or take other calling thread poll operation will be
blocked pending.
Code (2) to determine if the queue is empty the current thread blocked pending, and the current thread into notEmpty strip
member queue.
Code (3) and counts dequeue operation.
Code (4) determines if c> l then the current queue is not empty, then the wake-up conditions which notEmpty queue
a call to take because of the operation of the blocked thread.
Code (5) to release the lock.
Code (6) c == capacity determination if it indicates the current queue has at least one idle position, the activation bar
member notFull variable conditions inside the queue because a call to put operation is blocked thread.

  1. reomove () operation can not be interrupted
    public Boolean Remove (Object O) {
    // returns empty to false
    IF (O == null) return to false;
    // Get lock. 1 - double lock.
    fullyLock ();
    the try {
    // 2 traversing delete queue to find and return to true
    for (= the Node TRAIL head, P = trail.next;
    P = null;!
    TRAIL = P, P = p.next) {
    //. 3
    IF (o.equals (p.item )) {
    the unlink (P, TRAIL);
    return to true;
    }
    }
    // if not found. 4 SE iii Press FAL
    return to false;
    } the finally {
    // unlock. 5
    fullyUnlock ();
    }
    }

双重锁
void fullyLock() {
putLock.lock();
takeLock.lock();
}
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}

Code (1) acquired by the double lock as HyLock, after acquisition, other threads enqueue or dequeue operation 'would
be blocked pending.

Code (2) traverse the queue to find the element you want to delete, then could not find a direct returns false, find unlink operation is performed
void unlink (the p-the Node, the Node TRAIL) {
// isFullyLocked the Assert ();
// p.next IS not changed , to the allow iterators that are
// Traversing to Maintain Their weak-P Consistency Guarantee.
p.item = null;
trail.next = p.next;
IF (Last == P)
Last = TRAIL;
// if the current queue is full, after the deletion, do not forget to wake up waiting threads
IF (count.getAndDecrement () == Capacity)
notFull.signal ();
}

After removing elements, if we find the current queue has free space, the wake-up condition queue notFull in a call to put methods because the blocked thread
Code (5) and locking method call fullyUnlock the order to release a double reverse lock

Due to the remove method plus two locks before deleting the specified element, so in walks the queue to find a specific dollar
is a prime in the process of thread-safe, and the other at this time to call into the team, the team operated all threads will be blocked. Additionally,
acquiring a plurality of resource lock release and the order is reversed

size 操 作
public int size() {
return count.get();
}

As a result of the team, count operation when the team added a lock, so the results compared ConcurrentLinkedQueue the
size method is relatively accurate.

Guess you like

Origin blog.csdn.net/ko0491/article/details/90903428