In layman's Java Concurrency (22): Concurrent container Part 7 can be blocked Blockingqueue (2) [Go]

Source: Internet
Author: User

The implementation principle of Linkedblockingqueue is analyzed in detail in the previous section. There are usually two ways to implement an extensible queue: one is to use a linked list like linkedblockingqueue, which means that each element has a reference to the next element, so that the queue is natively extensible, and the other is implemented by an array, Once the size of the queue reaches the capacity of the array, the array is expanded one time (or a certain number of times) to achieve the purpose of expansion. Common ArrayList belong to the second kind. The HashMap described in the previous chapters are a combination of these two approaches.

For a queue, you can also use an array implementation. The advantage of using an array is that the primitives are associated with the index of the array, and the elements are ordered between them, and it is much easier to manipulate the array through the index. Of course there is an unfavorable side, the expansion of the more troublesome, while deleting an element is also relatively inefficient.

Arrayblockingqueue is an array implementation of the queue.

Arrayblockingqueue principle

Before you introduce the arrayblockingqueue principle, imagine how an array implements the queue's FIFO feature. First of all, the array is fixed size, this is no doubt, then the initialization is all the elements are null. Suppose the array is a header and the other end is a tail. Then the element between the head and tail is the FIFO queue.

      1. Into the queue to move the tail index to the right one, the new element added to the position of the tail index;
      2. The out queue moves the head index toward the tail index direction, while the old header index element is set to NULL, returning the elements of the old header index.
      3. Once the array is full, adding new elements is not allowed (unless capacity is expanded)
      4. If the tail index is moved to the end of the array (at the maximum index), it starts at index 0 and forms a "closed" array.
      5. Because neither the element between the header index nor the tail index can be empty (because NULL does not know whether the take-out element is empty or the queue is empty), deleting an element between the head index and the tail index requires moving all elements before or after the delete index in order to populate the location where the index is deleted.
      6. Because it is blocking the queue, it is clear that a lock is required, and because it is only a single piece of data (an array), there can only be one lock, which means that only one thread can operate the queue at a time.

With the above analysis, it is easier to design a block array queue.

Describes the data structure of the arrayblockingqueue. First there is an array e[], which is used to store all the elements. Since Arrayblockingqueue is eventually set to a non-expandable queue, items Here are initialized with fixed-size arrays (final type), and two indexes, header index Takeindex, tail index Putindex ; The size of a queue count; To support blocking, you must require a lock lock and two conditions (non-empty, non-full), and these three elements are immutable types (final).

Because there is only one lock, there is only one thread for the queue operation at any moment, which means that the operation of the index and size is thread-safe, so you can see that the takeindex/putindex/count does not require atomic manipulation and volatile semantics.

Listing 1 describes a blocking process for adding elements. This is the same as the consumer and producer models described earlier. If the queue is already full, suspend the wait, or insert the element while waking up a thread that is empty from the queue. Compare Listing 2 to see the two processes that are completely reversed. This has been introduced in several previous implementations of producer-consumer models.

Listing 1 blocks of added elements

public void put (e e) throws interruptedexception {
if (E = = null) throw new NullPointerException ();
Final e[] items = this.items;
Final Reentrantlock lock = This.lock;
Lock.lockinterruptibly ();
try {
try {
while (count = = items.length)
Notfull.await ();
} catch (Interruptedexception IE) {
Notfull.signal (); Propagate to non-interrupted thread
throw ie;
}
Insert (e);
} finally {
Lock.unlock ();
}
}

Listing 2 removable elements that can be blocked

Public E take () throws Interruptedexception {
Final Reentrantlock lock = This.lock;
Lock.lockinterruptibly ();
try {
try {
while (count = = 0)
Notempty.await ();
} catch (Interruptedexception IE) {
Notempty.signal (); Propagate to non-interrupted thread
throw ie;
}
E x = extract ();
return x;
} finally {
Lock.unlock ();
}
}

It is important to note that even though each addition and removal of an element uses the signal () notification instead of the Signalall () notification. We refer to the principle of replacing notifyall in the previous section: Each notify wakes up with the same action, waking up one thread at a time. Obviously these two conditions are met, so using signal is more efficient and reliable than using Signalall.

Describes the index position of Take ()/put ().

At first Takeindex/putindex are all in the e/0 position, then each added element Offer/put,putindex increases by 1, that is, to move one bit back, and each removed element poll/take,takeindex is incremented by 1, is also moving backward, obviously takeindex always in the Putindex "behind", because when there is no element in the queue Takeindex and Putindex equal, while the current position also has no elements, takeindex that can no longer move to the right Once the Putindex/takeindex is moved to the last surface, i.e. the position of size-1 (where size is the length of the exponential group), move to 0 and continue the loop. The premise of the loop is that the number of elements in the array is less than the length of the array. This is the whole process. It is visible that the Putindex points to the next position of the head element (if the queue is full, the trailing element is the position of the element, otherwise it is a null position).

Delete any element when comparing complex operations. Listing 3 describes the process of deleting an arbitrary element. Obviously deleting any element requires traversing the entire array, i.e. its complexity is O (n), which is much more expensive than finding the complexity O (1) of an element from ArrayList based on the index. Refer to the structure of the Declaration, once deleted is the element of the Takeindex position, then only need to move the takeindex to "right" one can, if the deletion is the element between Takeindex and putindex what to do? At this point, starting from the deleted position I, all the element positions after I are moved to "left" one bit until putindex. The end result is that all elements of the delete position "back" a position, while the Putindex also backs up a position.

Listing 3 Deleting any one element

public boolean remove (Object o) {
if (o = = null) return false;
Final e[] items = this.items;
Final Reentrantlock lock = This.lock;
Lock.lock ();
try {
int i = Takeindex;
int k = 0;
for (;;) {
if (k++ >= count)
return false;
if (O.equals (Items[i])) {
RemoveAt (i);
return true;
}
i = Inc (i);
}

} finally {
Lock.unlock ();
}
}
void removeAt (int i) {
Final e[] items = this.items;
If removing front item, just advance
if (i = = Takeindex) {
Items[takeindex] = null;
Takeindex = Inc (TAKEINDEX);
} else {
Slide to others up through Putindex.
for (;;) {
int nexti = Inc (i);
if (Nexti! = Putindex) {
Items[i] = Items[nexti];
i = Nexti;
} else {
Items[i] = null;
Putindex = i;
Break
}
}
}
--count;
Notfull.signal ();
}

For the other operations, because they are all with the lock operation, so it is relatively simple to no longer expand.

The next two blockingqueue, Priorityblockingqueue and Synchronousqueue, then make a small-scale comparison of these common queues.

In layman's Java Concurrency (22): Concurrent container Part 7 can be blocked Blockingqueue (2) [Go]

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.