Inserting __java in Michael-scott non-blocking queue algorithm

Source: Internet
Author: User
Tags cas static class

Basic usage patterns for CAS: there is uncertainty when updating a value, and try again when an update fails. The trick to building non-blocking algorithms is to narrow the range of atomic modifications to a single variable.

A link queue is more complex than a stack because it must support fast access to the head and tail nodes. Therefore, it requires a separate maintenance of the head and tail pointers. There are two pointers to the trailing node: the next pointer to the current last element, and the tail node. When a new element is successfully inserted, both pointers need to be updated with atomic operations.

Here's the trick to do it, and the first trick is to make sure that the data structure is always in a consistent state, even in an update operation that contains multiple steps. Thus, if thread A is found to be performing an update when thread B arrives, thread B can know that one of the operations is partially completed and cannot begin its own update operation immediately. B can then wait (by repeatedly checking the status of the queue) and until a completes the update so that two threads do not interfere with each other.

Although this approach enables different threads to "rotate" access to the data structure and does not cause damage, if one thread fails in the update operation, no other thread can access the queue. To make the algorithm a non-blocking algorithm, you must ensure that a thread fails without preventing other threads from continuing. So the second trick is that if you find that a is modifying the data structure when B arrives, there should be enough information in the data structure so that B can complete A's update operation. If B "Help" A completes the update operation, B can perform its own operation without waiting for the operation of a to complete. When a is restored and then attempts to complete its operation, B is found to have done it.

In the following program, the insertion part of the non-blocking Michael-scott algorithm is presented, which is implemented by Concurrentlinkedqueue. In many queue algorithms, empty queues usually contain a "sentinel node" or a "dumb (Dummy) node", and both the head and tail nodes point to the Sentinel node during initialization. The tail node usually either points to the Sentinel node (if the queue is empty), the last element of the queue, or (when an operation is performing an update) points to the penultimate element. Figure 1 below shows a two-element queue in a normal (or stable) state. Insert in Michael-scott non-blocking queue algorithm:

 
1 @ThreadSafe 2 public class Linkedqueue<e> {3 private static class Node <E> {4 final E item;
 5 Final atomicreference<linkedqueue.node<e>> Next;
 6 7 Public Node (E item, linkedqueue.node<e> next) {8 This.item = Item;
9 This.next = new atomicreference<linkedqueue.node<e>> (next); The private final linkedqueue.node<e> dummy = new linkedqueue.node<e> (null, NULL)
; Private final atomicreference<linkedqueue.node<e>> = new ATOMICREFERENCE&LT;LINKEDQ Ueue.
Node<e>> (dummy); Private final atomicreference<linkedqueue.node<e>> tail = new ATOMICREFERENCE&LT;LINKEDQ Ueue.
Node<e>> (dummy);  Public Boolean put (E item) {linkedqueue.node<e> NewNode = new Linkedqueue.node<e> (item,
NULL); while (true) {Linkedqueue.nodE<e> curtail = Tail.get ();
linkedqueue.node<e> Tailnext = CurTail.next.get (); if (curtail = = Tail.get ()) {(Tailnext!= null) {//A 26//at the queue In the middle state, the propulsion Tail node is tail.compareandset (curtail, tailnext); B/Else {29//is in a stable state, try inserting the new node if (CurTail.next.compareA Ndset (NULL, NewNode)) {//C 31//Insert operation successful, try to push tail node Tail.compareandset (CU Rtail, NewNode);
D return true; 34} 35} 36} 37} 38} 39}

Figure 1 is in a stable state and contains two elements of the opposition

When you insert a new element, you need to update the two pointers. The next pointer to the current last element is updated first, the new node is linked to the queue tail, and then the tail node is updated to point to the new element. Between two operations, the queue is in an intermediate state, as shown in Figure 2. Once the two updates have been completed, the queue will be stabilized again, as shown in Figure 3.

The key to implementing these two techniques is that when the queue is in a stable state, the next field of the tail node will be empty, and if the queue is in the middle, then the Tail.next will be non-empty. Therefore, any thread can check the Tail.next to get the current state of the queue. Also, when the queue is in the middle state, you can end the Insert element operations that other threads are executing by moving the tail node one node, and the queue is restored to a stable state.

Figure 2 The opposition in the middle State during the insert process

Figure 3 The queue is stabilized again after the insert operation completes

The Linkedqueue.put method first checks whether the queue is in the middle State (step a) before inserting a new element. If so, then another thread is inserting the element (between steps c and D). At this point, the current thread will not wait for the other thread to finish, but will help it complete the operation and push the tail node forward one node (step b). It then repeats this check so that another thread has started inserting the new element and continues to push the tail node until it finds that the queue is in a stable state before it begins to perform its own insert operation.

Because the CAs in step c will link the new node to the tail of the queue, the CAS will fail if two threads insert the element at the same time. In such cases, there is no damage: No changes will occur, and the current thread only needs to reread the tail node and try again. If step c succeeds, the insert operation takes effect, and the second CAs (step D) is considered a "cleanup operation" because it can be executed either by the thread executing the insert operation or by any other thread. If step d fails, the thread that executes the insert operation returns instead of performing the CAS again because it is no longer necessary to retry-another thread has completed the work in step b.

This works because before any thread attempts to insert a new node into the queue, it first checks to see if the queue needs to be cleaned by checking whether Tail.next is non-null. If it is, it will first recommend the tail node (which may need to be performed more than once) until the queue is in a stable state.

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.