Http://www.cnblogs.com/549294286/p/3766717.html
Exclusive lock: Is a pessimistic lock, synchronized is an exclusive lock that causes all other threads that need to be locked to hang, waiting for the thread that holds the lock to release the lock.
Optimistic lock: Each time without locking, assuming there is no conflict to complete an operation, if the conflict failed to retry until successful.
One, CAS operations
The mechanism used for optimistic locking is Cas,compare and Swap.
CAS has 3 operands, a memory value of V, an old expected value of a, and a new value to be modified B. If and only if the expected value A and the memory value of the V phase, the memory value of V is modified to B, otherwise do nothing.
1, non-blocking algorithm (nonblocking algorithms)
Failure or suspend of one thread should not affect other threads ' failed or suspended algorithms.
Modern CPUs provide special instructions to automatically update shared data and detect interference from other threads, and Compareandset () replaces the lock with these.
2. Atomicinteger Example
Take out Atomicinteger to study how the data is correct without a lock.
private volatile int value;
In the absence of a lock mechanism, it is necessary to use the volatile primitives to ensure that data between threads is visible (shared).
This allows the value of the variable to be read directly when it is obtained.
Public final int Get () { return value;}
Then let's see how ++i did it.
Public final int Incrementandget () {for (;;) { int current = Get (); int next = current + 1; if (Compareandset (current, next)) return next;} }
The CAS operation is used here, each time the data is read from memory and then the CAS operation is performed with the result of +1, if successful, the result is returned, otherwise it is retried until it succeeds.
Instead, Compareandset uses JNI to perform the operation of the CPU instructions.
Public final Boolean compareandset (int expect, int. update) { return unsafe.compareandswapint (this, Valueoffset, expect, update);}
The whole process is like this, using the CPU's CAS instruction, and using JNI to complete the Java non-blocking algorithm. Other atomic operations are done using a similar feature.
And the whole j.u.c is built on the CAs, so for synchronized blocking algorithm, J.U.C has a great improvement in performance. The resources article describes the use of CAs to build data structures such as non-blocking counters, queues, and so on.
Second, the problem of ABA
CAS looks cool, but it can cause "ABA problems".
The CAS algorithm implements an important premise that needs to take out the data at some point in memory, and compare and replace at the next moment, then the time difference class will cause the data to change.
For example, a thread one takes a from the memory location V, when another thread two also takes a from memory, and two does something to B, and two then turns the data in the V position into a, when the CAS operation finds that the memory is still a, and then the one operation succeeds. Although the CAS operation of thread one is successful, it does not mean that the process is not a problem. If the head of the list is changed two times, the original value is restored, but the list does not change. So the atomic operation Atomicstampedreference/atomicmarkablereference mentioned earlier is very useful. This allows a pair of changed elements to operate atomically.
There is a classic ABA problem in using CAs to do lock-free operations:
Thread 1 Prepares the value of the variable to be replaced by a B with a CAs, before which thread 2 replaces the value of the variable by a with C, and C with a, and then thread 1 executes the CAS when the value of the variable is found to still be a, so the CAS succeeds. But in fact the scene at this time is different from the original, despite the success of CAs, there may be hidden problems, such as the following example:
Existing a one-way list implementation of the stack, the stack top is a, then thread T1 already know A.next B, and then want to use CAs to replace the top of the stack with B:
Head.compareandset (A, b);
Before T1 executes this instruction, the thread T2 intervenes, a, b out of the stack, then pushd, C, A, the stack structure, and object B is in a free state at this time:
At this point the thread T1 performs the CAS operation, the detection finds that the top of the stack is still a, so the CAs succeeds, the stack top changes to B, but in fact the b.next is null, so this situation becomes:
Where there is only a B element in the stack, a list of C and D no longer exists on the stack, and C and D are discarded for no apparent matter.
The above is due to the problem of ABA problems, the implementation of various optimistic locks are usually used in version to mark the record or object, to avoid the problems caused by concurrency, in Java,atomicstampedreference<e> also achieve this role, It avoids the ABA problem by wrapping the tuple of [E,integer] into the OBJECT tag version stamp stamp, for example, the following code updates the atomic integer variable with an initial value of 100, respectively, using Atomicinteger and atomicstampedreference. , Atomicinteger will perform the CAS operation successfully, and the atomicstampedreference with the version stamp will perform a CAS failure for the ABA issue:
Package Concur.lock;import Java.util.concurrent.timeunit;import Java.util.concurrent.atomic.atomicinteger;import Java.util.concurrent.atomic.atomicstampedreference;public class ABA {private static Atomicinteger Atomicint = new Atomicinteger (100); private static atomicstampedreference<integer> Atomicstampedref = new atomicstampedreference<integer& gt; (100, 0); public static void Main (string[] args) throws Interruptedexception {thread intT1 = new Thread (new Runnable () { @Override public void Run () {atomicint.compareandset (100, 101); Atomicint.compareandset (101, 100); } }); Thread intT2 = new Thread (new Runnable () {@Override public void run () {try { TimeUnit.SECONDS.sleep (1); } catch (Interruptedexception e) {e.printstacktrace (); } Boolean C3 = Atomicint.compareandset (100, 101); System.out.println (C3); true}); Intt1.start (); Intt2.start (); Intt1.join (); Intt2.join (); Thread refT1 = new Thread (new Runnable () {@Override public void run () {try { TimeUnit.SECONDS.sleep (1); } catch (Interruptedexception e) {e.printstacktrace (); } atomicstampedref.compareandset (101, Atomicstampedref.getstamp (), Atomicstamp Edref.getstamp () +1); Atomicstampedref.compareandset (101, Atomicstampedref.getstamp (), Atomicstampedref.getstamp () + 1); } }); Thread refT2 = new Thread (new Runnable () {@Override public void run () {int stamp = Atomicstampedref.getstamp (); System.out.println ("Before sleep:stamp =" + stamp); Stamp = 0 try {TimeUnit.SECONDS.sleep (2); } catch (Interruptedexception e) {e.printstacktrace (); } System.out.println ("After sleep:stamp =" + Atomicstampedref.getstamp ());//stamp = 1 Boo Lean C3 = Atomicstampedref.compareandset (101, Stamp, stamp+1); System.out.println (C3); False}}); Reft1.start (); Reft2.start (); }}
Reference:
Introduction to non-blocking algorithms
The popular Atom
In layman's Java Concurrency (5): Atomic Operation Part 4
Solving ABA problem with atomicstampedreference
Java CAS and ABA issues