An article easily understands spin locks in Java

  • 2021-07-26 07:41:39
  • OfStack

Preface

As a tool to share data concurrently and ensure uniformity, locks have many implementations on JAVA platform (such as synchronized and ReentrantLock, etc.). These locks, which have been written and provided, provide convenience for our development.

All kinds of "locks" in Java were introduced in the previous article "One article thoroughly understands all kinds of" locks "often asked in interviews", which may be a bit confusing for students who don't know these concepts very well, so I decided to split them up and introduce the ins and outs of these locks in detail step by step, so this article will first meet the "spin locks".

Text

Causes of occurrence

In our program, if there are a large number of mutually exclusive synchronization codes, when there is high concurrency, the kernel state of the system needs to suspend threads and resume threads constantly, and frequent such operations will have a certain impact on the concurrency performance of our system. At the same time, the smart JVM development team also found that the time slice for locking "shared resources" during the execution of the program is extremely short. If the thread is continuously suspended and resumed just for this time, the time consumed may be longer, which means "picking up sesame seeds and losing watermelon".

In a multi-core machine, multiple threads can be executed in parallel. If, when the later thread requesting the lock does not get the lock, it does not suspend the thread, but continues to occupy the execution time of the processor, allowing the current thread to perform a busy cycle (spin operation), that is, constantly staring at whether the thread holding the lock has released the lock, then this is the legendary spin lock.

Spin lock opening

Although the spin lock was introduced in JDK 1.4. 2, it needs to be opened with the parameter "-XX: + UseSpinning". After JDK 1.6, it is already turned on by default. Let's implement a simple version of spin lock based on CAS.


public class SimpleSpinningLock {

 /**
 *  The thread holding the lock, null Indicates that the lock is not held by the thread 
 */
 private AtomicReference<Thread> ref = new AtomicReference<>();

 public void lock(){
 Thread currentThread = Thread.currentThread();
 while(!ref.compareAndSet(null, currentThread)){
  // When ref For null At the time of compareAndSet Return true Or vice versa false
  // Determine whether the lock is held by other threads through cyclic spin 
 }
 }

 public void unLock() {
 Thread cur = Thread.currentThread();
 if(ref.get() != cur){
  //exception ...
 }
 ref.set(null);
 }
}

A simple spin lock is realized in a few lines of code. Let's test 1


public class TestLock {

 static int count = 0;

 public static void main(String[] args) throws InterruptedException {
 ExecutorService executorService = Executors.newFixedThreadPool(100);
 CountDownLatch countDownLatch = new CountDownLatch(100);
 SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock();
 for (int i = 0 ; i < 100 ; i++){
  executorService.execute(new Runnable() {
  @Override
  public void run() {
   simpleSpinningLock.lock();
   ++count;
   simpleSpinningLock.unLock();
   countDownLatch.countDown();
  }
  });

 }
 countDownLatch.await();
 System.out.println(count);
 }
}

//  The output of multiple executions is: 100  The basic function of lock is realized 

As can be seen from the above code, spin is to judge whether the condition is met in a loop, so what's the problem? If the lock is held for a long time, the spinning thread waits longer, wasting processor resources. Therefore, in JDK, the spin operation defaults to 10 times, which can be set by the parameter "-XX: PreBlockSpin". When the value of this parameter is exceeded, the traditional thread suspension method will be used to wait for the lock to be released.

Adaptive spin lock

With the update of JDK, at 1.6, another gadget called "adaptive spin lock" appeared. Its appearance makes spin operation smart, and it is no longer as rigid as before. The so-called "adaptive" means that for the same lock object, the spin time of the thread is determined according to the spin time and state of the last thread holding the lock. For example, for A lock object, if a thread has just acquired the lock through spinning, and the thread is also running, JVM will think that this spinning operation also has a great chance to acquire the lock, so it will make the spinning time relatively prolonged. But if spin operations on B lock objects are rarely successful, JVM may even ignore spin operations directly. Therefore, the adaptive spin lock is a more intelligent and friendly lock for our business performance.

Conclusion

I originally thought about putting "spin lock" in an article, Some concepts of lock optimization, such as "lock elimination" and "lock coarsening", have been introduced, but it is found that the length may be relatively large, and it will be difficult for students who have not touched this piece to understand it, so they decided to introduce it separately in several chapters. I hope everyone can read it several times and experience it slowly. I believe you will gain something.

Summarize


Related articles: