Talking about high concurrency (v.) Understanding of Cache consistency protocol and its impact on concurrent programming we understand the principle of the processor cache consistency protocol, it also refers to its impact on concurrent programming, "Multiple threads have been using CAs for the same variable, so there will be a lot of modifications, resulting in a lot of cache consistency traffic, Because each CAS operation emits a broadcast notification to other processors, which affects the performance of the program. ”
In this article we have two ways to achieve the spin lock to see the different programming methods brought about by the changes in program performance.
First understand what is the spin, the so-called spin is a thread in the case of not satisfying a certain condition, has been looping to do some action. So for a spin lock to lock, when the thread is not acquiring a lock, the loop attempts to acquire the lock until the lock is actually acquired.
Talking about some basic concepts of high concurrency (iii) lock we mentioned that the nature of the lock is to wait, then how to wait, there are two ways
1. Thread Blocking
2. Thread Spin
The disadvantage of blocking is obvious, once the thread has entered the blocking (block), the higher the cost of awakening, poor performance. The advantage of spin is that the thread is still runnable, just executing the empty code. Of course, the spin will also be used in vain computing resources, so the common practice is to spin for a period of time, not to get the lock into the block. The JVM uses this eclectic approach when dealing with synchrized implementations and provides parameters to adjust the spin.
This article explains the two most basic spin-lock implementations, and provides an optimized lock, followed by more spin-lock implementations.
First is Taslock (test and set lock), testing-Set the lock, it is characterized by spin, each attempt to acquire a lock, the use of CAS operation, constantly set the lock flag bit, when the lock flag bit available, one thread to get the lock, other threads continue to spin.
The disadvantage is that the CAS operation has been modifying the value of the shared variable, causing a cache-consistent traffic storm
Package com.test.lock;
Lock interface Public
interface Lock {public
void Lock ();
public
void Unlock ();
}
Package com.test.lock;
Import Java.util.concurrent.atomic.AtomicBoolean;
/**
* Test-set spin lock, save state with Atomicboolean atomic variable
* Every time you use the Getandset atomic operation to determine the lock state and try to acquire
the lock * The disadvantage is that the Getandset bottom is implemented using CAs. The value of the shared variable has been modified to raise the cache consistency traffic Storm
* **/public
class Taslock implements lock{
private Atomicboolean mutex = new Atomicboolean (false);
@Override
the public void Lock () {
//Getandset method sets the mutex variable to TRUE and returns the value before the mutex
//When the mutex is false before returning, Indicates that the acquire lock
//Getandset method is atomic and the change of the mutex atom variable is visible to all threads while
(Mutex.getandset (True)) {
}
}
@ Override public
void Unlock () {
mutex.set (false);
}
Public String toString () {return
"Taslock";
}
}
An improved algorithm is Ttaslock (test test and set lock) testing-testing-setting locks, the feature is that when a spin attempts to acquire a lock, it is divided into two steps: The first step is to acquire the lock state by reading, and when the lock is available, the second step is to attempt to acquire the lock by the CAS operation, Reduces the number of operations for CAs. And the first step of the read operation is the processor directly read its own cache, does not generate cache consistency flow, does not occupy the bus resources.
The disadvantage is that in the case of lock high contention, it is difficult for a thread to acquire a lock at a time, and the operation of CAs can be greatly increased.
Package com.test.lock;
Import Java.util.concurrent.atomic.AtomicBoolean;
/**
* Test-test-set spin lock, use Atomicboolean atomic variable to save state
* is divided into two steps to obtain the lock
* 1. The first attempt to acquire the lock * 2 by means of the read variable spin.
when it is possible to acquire a lock, Using the Getandset atomic operation to try to acquire the lock
* Advantage is the first step is to use read variables to acquire the lock, cache the operation inside the processor, do not generate cache consistency Traffic
* The disadvantage is that when the lock contention is fierce, the first step is not to get the lock, Getandset the bottom layer is implemented using CAs to modify the value of the shared variable all the time, raising the cache consistency traffic Storm
* **/public
class Ttaslock implements lock{
private Atomicboolean Mutex = new Atomicboolean (false);
@Override public
void Lock () {
while (true) {
//The first step is to use a read operation, attempt to acquire a lock, and exit the loop when the mutex is false, indicating that the lock while
( Mutex.get ()) {}
//second uses the Getandset method to attempt to acquire the lock
if (!mutex.getandset (True)) {return
;
}
}
} @Override public
void Unlock () {
mutex.set (false);
}
Public String toString () {return
"Ttaslock";
}
}
For the lock high contention problem, you can take a fallback algorithm, that is, when the thread did not get the lock, wait a while to try to acquire the lock, which can reduce the contention of the lock, improve the performance of the program.
Package com.test.lock;
Import Java.util.Random;
/** * fallback algorithm, reduce the probability of lock contention * **/public class Backoff {private final int mindelay, maxdelay;
private int limit;
Final Random Random;
public backoff (int min, int max) {this.mindelay = min;
This.maxdelay = max;
Limit = Mindelay;
Random = new Random ();
//fallback, thread waits for a period of time public void Backoff () throws interruptedexception{int delay = random.nextint (limit);
Limit = Math.min (maxdelay, 2 * limit);
Thread.Sleep (delay);
}} package Com.test.lock;
Import Java.util.concurrent.atomic.AtomicBoolean; /** * Rewind spin lock, increased thread fallback on test-test-set spin lock, lower lock contention * The advantage is that lock contention is reduced in the case of high contention, and performance is improved * the drawback is that the time for rollback is difficult to control, Constant testing is required to find the right value and relies on the performance of the underlying hardware, poor scalability * **/public class Backofflock implements lock{ private final in
T Min_delay, Max_delay; public backofflock (int min, int max) { min_
DELAY = min; max_delaY = max;
 } private Atomicboolean mutex = new Atomicboolean (false); @Override public void Lock () {
//Add Fallback object backoff backoff = new Backoff (Min_delay, Max_delay); while (True) { // The first step is to use the read operation, attempt to acquire the lock, and exit the loop when the mutex is false, indicating that the lock while (mutex.get) can be acquired ( ) {} //The second part uses the Getandset method to try to acquire the lock if (!mutex.getandset (True)) {
return; }else{ //fallback try {
backoff.backoff ();  } catch (Interruptedexception e) { & nbsp } & nbsp;}  } & nbsp;  } @Override public void Unlock () { &nbs P
mutex.set (FALSE);  } public String toString () { return "
Ttaslock ";  }}
The problem with the rollback spin lock is that the rollback time is difficult to control, requiring constant testing to find the right value, and depending on the performance of the underlying hardware and poor scalability. A better spin-lock implementation algorithm will be available later.
Below we test the performance of Taslock and Ttaslock.
First, write a timer class
Package com.test.lock;
public class Timecost implements lock{
private final lock lock;
Public Timecost (lock Lock) {
This.lock = lock;
}
@Override public
void Lock () {
Long start = System.nanotime ();
Lock.lock ();
Long Duration = System.nanotime ()-Start;
System.out.println (lock.tostring () + "time Cost is" + Duration + "ns");
}
@Override public
void Unlock () {
lock.unlock ();
}
}
Multiple threads are then used to simulate contention for the same lock
Package com.test.lock;
public class Main {
private static timecost timecost = new Timecost (new Taslock ());
private static Timecost Timecost = new Timecost (new Ttaslock ());
public static void Method () {
timecost.lock ();
int a = ten;
Timecost.unlock ();
}
public static void Main (string[] args) {for
(int i = 0; i < i + +) {
thread t = new Thread (new Runnable () { c11/> @Override public
void Run () {method
();
}
});
T.start ();}}
The performance of the test machine is as follows:
Cpu:4 Intel (R) Core (TM) i3-2120 CPU @ 3.30GHz
Memory: 8G
Test results:
50 Threads:
Taslock average time to acquire a lock: 339715 NS
Ttaslock average time to acquire a lock: 67106.2 NS
100 Threads:
Taslock average time to acquire a lock: 1198413 NS
Ttaslock average time to acquire a lock: 1273588 NS
You can see that ttaslock performance is better than taslock performance
Reprint please indicate the source: HTTP://BLOG.CSDN.NET/ITER_ZC