Non-blocking synchronization mechanism
To put it simply, it's about synchronizing and not using locks.
The implementation of a nonblocking algorithm is much more cumbersome than a lock-based scheme, but it has a huge advantage in scalability and activity.
A common way to implement non-blocking algorithms is to use volatile semantics and atomic variables.
Hardware-to-concurrency support
The generation of atomic variables is primarily supported by the processor, and most importantly the CAS (compare and exchange) directives that are supported by most processor architectures.
Analog implementation of Atomicinteger + + operations
First we simulate the CAS syntax of the processor, and the reason is that the CAs are directly supported by atomic operations in the processor. No lock required.
- public synchronized int Compareandswap (int exceptvalue, int newvalue) {
- int oldValue = count;
- if (OldValue = = Exceptvalue) {
- Count = newvalue;
- }
- return oldValue;
- }
Note: CAs always returns oldValue.
Use the above method to simulate the Atomicinteger + + operation.
- Class myatomicinteger{
- private int value;
- public int Incrementandget ()
- {
- int v;
- do{
- v = value;
- }while (v! = Compareandswap (v,v+1));
- return v + 1;
- }
- }
Note: Java's Atomicinteger Implementation mechanism is like this, does not block, uses the processor's CAS function, but polls the attempt.
It seems that polling attempts are worse, but the CAS-based algorithm is better when the competition is not very high.
Atomic class
Atomicboolean Atomicinteger Atomiclong atomicreference
Atomic Array class: Atomicintegerarray Atomiclong atomicreferencearray.
Arrays decorated with volatile syntax can only guarantee the volatile semantics of the array variables themselves, and cannot guarantee the volatile semantics of the elements. The atomic array class should be used at this time.
Note: Atomicboolean Atomicinteger Atomiclong and non-atomic corresponding numeric classes such as Integer are completely unused. The implementation mechanism is completely different and there is no corresponding relationship. One of the most important differences: This three-atom class is mutable. and is the hashcode and Equals method of the object used, which does not extend itself.
Performance comparisons: Locks and Atomic variables
In the medium to low level competition, the atom variable can provide the very high scalability, the Atom variable performance surpasses the lock, but under the intense competition, the lock can avoid the competition more effectively, the lock performance will surpass the atom variable performance. But in a more realistic situation (generally less powerful competition), the performance of the atomic variable will exceed the performance of the lock.
Note: Regardless of whether it is a lock or an atomic variable, it is far less efficient than avoiding shared states (such as using thread-blocking techniques, but using scenarios to limit the usage) to eliminate competition.
Two non-blocking algorithm examples
- /**
- * Non-blocking stacks constructed using the Treiber algorithm
- */
- public class Concurrentstack<e> {
- private atomicreference<node<e>> top = new atomicreference<concurrentstack.node<e>> ();
- Public void Push (E item) {
- node<e> newhead = new node<e> (item);
- Node<e> Oldhead;
- do{
- Oldhead = top. get ();
- Newhead.next = Oldhead;
- } while (!top.compareandset (Oldhead, Newhead));
- }
- Public E Pop () {
- Node<e> Oldhead;
- Node<e> Newhead;
- Do {
- Oldhead = top. get ();
- if (Oldhead = = null)
- return null;
- Newhead = Oldhead.next;
- } while (!top.compareandset (Oldhead, Newhead));
- return oldhead.item;
- }
- private static Class node<e>{
- Public final E item;
- Public node<e> Next;
- Public Node (E item) {
- This.item = Item;
- }
- }
- }
Below this did not understand, stay here record, later look:
- /**
- * Insert sort in non-blocking algorithm of list, from Michael-scott
- */
- public class Linkedqueue<e> {
- private static Class node<e>{
- final E item;
- final atomicreference<node<e>> next;
- Public Node (E item, node<e> next) {
- This.item = Item;
- this.next = new atomicreference<> (next);
- }
- }
- private final node<e> dummy = new node<e> (null, NULL);
- Private final atomicreference<node<e>> head =
- new atomicreference<> (dummy);
- private final atomicreference<node<e>> tail =
- new atomicreference<> (dummy);
- Public Boolean put (E item) {
- node<e> NewNode = new node<e> (item, NULL);
- While (true) {
- Node<e> curtail = Tail.get ();
- node<e> Tailnext = CurTail.next.get ();
- if (curtail = = Tail.get ()) { //Trailer not modified
- if (tailnext! = null) {
- //queue is in the middle (i.e. the new node has been connected, the tail node has not been updated), push the tail node
- Tail.compareandset (curtail, tailnext);
- } else{
- //In a stable state, try inserting a new node
- if (CurTail.next.compareAndSet (null, NewNode)) {
- //After insert succeeds, push tail node
- Tail.compareandset (curtail, tailnext);
- return true;
- }
- }
- }
- }
- }
- }
Java concurrency Programming the 15th Chapter atom variable and non-blocking synchronization mechanism