preface
- Natural selection makes survival of the fittest. The JDK is also being refined. As for the internal optimization of synchronized lock in JDK, we have analyzed that biased lock is used to solve the initial problems, and lightweight lock operation emerged with the continuous accumulation of competition.
- Follow me, a progressive social code farmer, and take you out of the crisis
Lightweight lock
- It says that bias locks occur when there is no competition and bias locks are turned on. But bias locks are not automatically revoked. Let’s look at the next list
- The VM configuration is as follows
-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
public class SimpleTest { public static void main(String[] args) { SimpleTest test = new SimpleTest(); System.out.println(ClassLayout.parseInstance(test).toPrintable()); synchronized (test) { System.out.println("hello world"); System.out.println(ClassLayout.parseInstance(test).toPrintable()); } System. Out. Println (" after the lock release: "+ ClassLayout. ParseInstance (test). ToPrintable ()); }}Copy the code
- We can see that the Markword in the test object is always biased lock before, during, and after locking. That means there’s no voluntary withdrawal
- Given this premise, let’s imagine that there are two threads locking on the same object at different times. Is this called resource competition? Because not running at the same time actually happens interactively, but because bias locks are not released actively by default. In the process of bias locking, the current thread is written into Markword through CAS. Before writing, the lock object markword is compared to whether it belongs to the current thread. If the thread id is the same as the current thread ID, the counter is incremented by 1 to implement the reentrant lock.
- If it is the second thread, the thread ID will be inconsistent regardless of whether it is the same thread. This is when biased locks are upgraded to lightweight locks. This upgrade process is also a very troublesome process. The JVM actually needs to find the safe point (that is, the point in time when the thread is inactive) to undo the bias lock and then apply the lightweight lock
Biased lock diagram
Lightweight lock icon
- As can be seen from the diagram, biased locks only have one CAS, while lightweight locks have CAS all the time. We need to know that the thread spin caused by CAS also consumes CPU scheduling, because all threads are active, so CPU will have thread scheduling switch. So biased locking is funny in projects where concurrency is not high and common.
class User{ String userName; } public class SoftLock { public static void main(String[] args) throws InterruptedException { User user = new User(); Before System. Out. Println (" locked (disabled to delay, at this time should be biased locking default) : "+ ClassLayout. ParseInstance (user). ToPrintable ()); final Thread t1 = new Thread(new Runnable() { @Override public void run() { synchronized (user) { System. The out. Println (" t1 lock: "+ ClassLayout. ParseInstance (user). ToPrintable ()); }}}); t1.start(); t1.join(); final Thread t2 = new Thread(new Runnable() { @Override public void run() { synchronized (user) { System. Out. Println (" t1 lock, because after t1 locked thread to lock does not release, so t2 biased locking cancellation will happen, eventually t2 lightweight lock: "+ ClassLayout. ParseInstance (user). ToPrintable ()); }}}); t2.start(); t2.join(); System. The out. Println (" after the lock (unlocked) : "+ ClassLayout. ParseInstance (user). ToPrintable ()); }}Copy the code
- As you can see from the above code, trying to lock in a T2 thread becomes a lightweight lock. Lightweight locks differ from biased locks in that lightweight locks release the lock and become unlocked
- When a biased lock is accessed by another thread, the biased lock is upgraded to a lightweight lock, and the other thread attempts to acquire the lock in the form of spin without blocking, thus improving performance.
- When the code enters the synchronization block, if the Lock status of the synchronization object is lockless (the Lock flag bit is “01”, whether the bias Lock is “0”), the VIRTUAL machine will first establish a space named Lock Record in the stack frame of the current thread, which is used to store the copy of the current Mark Word of the Lock object. Then copy the Mark Word from the object header into the lock record.
- After the copy is successful, the VM attempts to update the Mark Word of the object to a pointer to the Lock Record using the CAS operation, and sets the owner pointer in the Lock Record to the Mark Word of the object.
- If the update succeeds, the thread owns the lock on the object, and the object’s Mark Word lock bit is set to 00, indicating that the object is in a lightweight locked state.
- If the lightweight lock fails to update, the virtual machine first checks whether the object’s Mark Word refers to the current thread’s stack frame. If it does, the current thread already owns the lock and can proceed directly to the synchronization block. Otherwise, multiple threads compete for the lock.
- If there is currently only one waiting thread, the thread waits through spin. But when the spin exceeds a certain number, or when one thread is holding the lock, one is spinning, and a third person is calling, the lightweight lock is upgraded to the heavyweight lock.
- Multiple threads request the same lock at different times, meaning there is no lock contention. In this case, the Java virtual machine uses lightweight locks to avoid blocking and waking up heavyweight locks
- The condition for a lightweight lock is to have a competition or to have to use a lightweight lock. Let’s take a look at a scenario where a lightweight lock has to be applied, noting that the VM attribute is enabled to bias the lock delay and the VM does not do any configuration
public class SimpleTest { public static void main(String[] args) { SimpleTest test = new SimpleTest(); System.out.println(ClassLayout.parseInstance(test).toPrintable()); synchronized (test) { System.out.println(ClassLayout.parseInstance(test).toPrintable()); }}}Copy the code
- This code is the same as the one above to demonstrate anonymous biased locking, except that the VM configuration is cancelled. That is, bias lock delay is turned on. So the markword in the test object we print for the first time is unlocked. It should have gone up the second time. But let’s imagine that the delay bias may also increase the bias lock during the second upward bias lock, so there will be a scramble for resources, in order to avoid conflict with the delay bias, so the second time is directly lightweight lock.
Subsequent iterations introduced heavyweight locks.