If you understand the synchronized keyword this way, will you understand it?

Talk about your understanding of the synchronized keyword

The synchronized keyword is used to solve the synchronization of accessing resources between multiple threads. The synchronized keyword can ensure that the method or code block modified by it can only be executed by one thread at any time .

It is worth noting that in the early days of Java, before JDK 1.6, synchronized was a heavyweight lock and was inefficient.

the reason is:

The monitor lock [monitor] relies on the Mutex Lock implementation of the underlying operating system. Java threads are mapped onto the native threads of the operating system. If you want to suspend or wake up a thread, you need the help of the operating system, and the operating system needs to switch from user mode to kernel mode when switching between threads, which takes a long time .

However, after JDK1.6, Java officially optimized the synchronized keyword from the JVM level, and the efficiency is not the same. The main optimizations are: spin lock, adaptive spin lock, lock elimination, lock coarsening, bias lock, lightweight lock and other technologies to reduce the overhead of lock operations.

Three uses of synchronized keyword

  1. Modification instance method: act on the current object instance to lock, and obtain the current object instance lock before entering the synchronization code  .
  2. Modification of static methods:  that is, to lock the current class, which will act on all object instances of the class, and obtain the lock of the current class before entering the synchronization code  .

Note: Static members do not belong to any instance object, they are class members! Therefore, it is allowed for a thread A to call the non-static synchronized method of an instance object, and a thread B to call the static synchronized method of the class to which the instance object belongs. Because the lock occupied by access to the static synchronized method is the lock of the current class, and the lock occupied by the access to the non-static synchronized method is the lock of the current instance object .

  1. Modified code block  : lock the object configured in brackets. synchronized(this|object) means to obtain the lock of the given object before entering the synchronized code base . synchronized(class.class) means to obtain the lock of the current class before entering the synchronization code  .

The underlying principle of the synchronized keyword

By compiling the .class file, you can find:

  • The synchronization method is modified by ACC_SYNCHRONIZED.
  • The code block is implemented synchronously using two instructions monitorenter and monitorexit.

Although the implementation details of the two are different, the essence is that the JVM implements synchronization based on entering and exiting the Monitor object . The requirements of the JVM are as follows:

  • The monitorenter instruction will be inserted at the beginning of the synchronization code block after compilation, and the monitorexit will be inserted at the end of the method and exception.
  • Every object has a monitor associated with it, and when a monitor is held, it will be locked.
  • When the thread executes to the monitorenter, it will try to obtain the ownership of the object corresponding to the monitor.
  • When acquiring the lock, if the object is not locked, or the current thread already owns the lock of the object (re-enter, it will not lock itself), the lock counter is increased by one, and when monitorexit is executed, the lock counter is decreased by one, and the count is Zero the lock is released.
  • If the object lock fails, the current thread is blocked until the object lock is released by another thread.

Optimization of the synchronized keyword after JDK1.6

Optimization: bias lock, lightweight lock, spin lock, adaptive spin lock, lock elimination, lock coarsening.

There are four main lock states, in order: no lock state, biased lock state, lightweight lock state, and heavyweight lock state . They will gradually upgrade with the fierce competition. Note that locks can be upgraded and cannot be downgraded . This strategy is to improve the efficiency of acquiring and releasing locks.

Composition of Java object header

The lock exists in the Java object header, which is part of the object header:

  • Mark Word: Store the hashCode or lock information of the object.
  • Class Metadata Address: A pointer to the data of the object type.
  • Array length: the length of the array (if the current object is an array)

The Java object header exists in the Java heap. The heap memory is divided into three parts: the object header , instance data, and alignment padding.

The composition of MarkWord

The MardWord of the Java object header records the related information of the object and the lock. In the unlocked state, the HashCode, generation age, and lock flag bit of the object are stored by default in the Mark Word of the Java object header . In a 64-bit JVM, Mark Word is 64 bit .

Summary of "Java Concurrent Programming" Synchronized Interview Questions

 

During operation, the data stored in the Mark Word will change with the change of the lock flag. The lock upgrade function is also mainly completed by the lock flag in MarkWord and whether it is biased toward the lock flag .

The process of lock upgrade

The process of lock upgrade: no lock, partial lock, lightweight lock, heavyweight lock

Summary of "Java Concurrent Programming" Synchronized Interview Questions

 

Bias lock

The author of HotSpot found through research that in most cases, the lock not only does not have multi-thread competition, but is always acquired by the same thread multiple times. In order to make the thread acquire the lock cheaper, a biased lock is introduced.

Applicable scenarios of bias lock

Biased locks are mainly used for optimization: the same thread applies for the same lock multiple times . In some cases, the same thread competes for lock resources most of the time.

Summary of "Java Concurrent Programming" Synchronized Interview Questions

 

Locking of biased locks

Main process: When a thread accesses the synchronization block and acquires the lock, the thread ID of the lock bias will be stored in the lock record in the object header and the stack frame. In the future, the thread does not need to perform CAS operations to add when entering and exiting the synchronization block. To lock and unlock , simply test whether there is a bias lock pointing to the current thread stored in the Mark Word of the object header .

  • If the test is successful, the thread has acquired the lock.
  • If the test fails, you need to test whether the bias lock flag in Mark Word is set to 1 (indicating that it is currently a bias lock): if it is not set, the CAS competition lock is used. If set, try to use CAS to point the bias lock of the object header to the current thread.

Revocation of bias lock

Once other threads compete for lock resources, the bias lock will be revoked . The cancellation of the biased lock may need to wait for the global security point [there is no bytecode being executed at this point in time].

  • First suspend the thread holding the lock , and then check whether the thread holding the biased lock is alive , if the thread is not in the active state , set the object header to a lock-free state.
  • If the thread holding the biased lock is still alive, the stack with the biased lock will be executed, traversing the lock records of the biased object, the lock records in the stack and the Mark Word of the object header are either re-biased to other threads or restored to lock-free or The marked object is not suitable as a bias lock, and finally wakes up the suspended thread .

Deflection lock closure

Biased lock is enabled by default in Java 6 and Java 7, but it is activated only a few seconds after the application starts. If necessary, you can use the JVM parameter to turn off the delay: -XX:BiasedLockingStartupDelay=0.

If it is usually in a state of competition, you can pass -XX:-UseBiasedLocking=false to enter the lightweight lock state.

Lightweight lock

If there is a biased lock, if another thread competes for the lock, and the thread ID in the object header MarkWord is different from the current thread ID, the thread will try the CAS operation to acquire the lock. The acquisition fails, indicating that there is a competition for the lock, and the bias is lighter. Upgraded weight lock .

Summary of "Java Concurrent Programming" Synchronized Interview Questions

 

Locking of lightweight locks

  • Thread prior to performing synchronized block, JVM will first created in the current thread's stack space for storing Taichung lock records] [Displaced Mark Word, and the head of Mark Word objects copied to lock the record .
  • Then the thread tries to use CAS to replace the Mark Word in the object header with a pointer to the lock record . If the replacement is successful, the current thread obtains the lock. The replacement fails, indicating that other threads compete for the lock, and the current thread tries to use spin to acquire the lock.

Unlocking lightweight locks

  • Use atomic CAS operation to replace [Displaced Mark Word] back to the head of the object. The replacement is successful, indicating that no competition occurs. Replacement failure means that the current lock has competition, and the lock will expand into a heavyweight lock.

Application scenarios of lightweight locks

Threads alternately execute synchronization blocks, and most locks do not have long-term competition throughout the synchronization cycle .

Comparison of advantages and disadvantages of locks

Advantages and disadvantages of locks Applicable scenarios: Locking and unlocking do not require additional consumption, and there is only a nanosecond gap compared with the implementation of asynchronous methods. If there is lock contention between threads, it will cause additional lock cancellation costs. It is suitable for scenarios where only one thread accesses the synchronized block. Threads competing for lightweight locks will not block, which improves the response speed of the program. If the competing thread is not always available, spin will consume CPU. In pursuit of response time. The synchronization block execution speed is very fast. The heavyweight lock thread competition does not use spin and does not consume CPU. The thread is blocked and the response time is slow. Pursue throughput. The synchronization block execution speed is longer.

to sum up

  1. JVM introduced a hierarchical lock mechanism in JDK 1.6 to optimize synchronized
  2. When a thread acquires a lock, the object lock first becomes a biased lock. This is to avoid frequent switching between user mode and kernel mode when the same lock is repeatedly acquired in the same thread
  3. If there are multiple threads competing resource lock, the lock will be upgraded to lightweight lock This applies in a short time to hold internal lock, and lock sub- toggling scenario also incorporates a lightweight lock spin lock to prevent users thread Frequent switching between state and kernel state
  4. If the lock competition is too fierce (spin lock fails), the synchronization lock will be upgraded to a heavyweight lock
  5. The key to optimizing synchronized synchronization locks: To reduce lock competition, synchronized synchronization locks should be made as lightweight or biased as possible, so as to improve the performance of synchronized synchronization locks. Common methods to reduce lock granularity : reduce lock contention, reduce lock holding time , and improve The success rate of synchronized synchronization locks acquiring lock resources during spinning, avoiding upgrades to heavyweight locks
  6. When lock competition is fierce , you can consider disabling bias locks and disabling spin locks

The difference between synchronized keyword and ReentrantLock

Common ground

  • Both are reentrant locks: you can acquire your own internal lock again [to avoid deadlock when a thread acquires the lock and tries to acquire the lock again]. Each time the same thread acquires a lock, the counter is incremented by one, the lock is released, the counter is decremented, and the count is 0, which means that the lock is completely released.

difference

  • Synchronized depends on JVM implementation, and ReentrantLock depends on API.
  • Compared to synchronized, ReentrantLock adds some advanced features. There are three main points: Waiting can be interrupted  : ReentrantLock provides a mechanism that can interrupt the thread waiting for the lock. This mechanism is implemented by lock.lockInterruptibly(). That is to say, the waiting thread can choose to give up waiting and deal with other things instead.  Realizable fair lock : ReentrantLock can specify whether it is a fair lock or an unfair lock. And synchronized can only be an unfair lock. The so-called fair lock is the first waiting thread to obtain the lock first. ReentrantLock is unfair by default. You can use the ReentrantLock (boolean fair) construction method of the ReentrantLock class to determine whether it is fair. Selective notification can be realized (locks can be bound to multiple conditions) : The synchronized keyword and the wait() and notify()/notifyAll() methods can be combined to realize the wait/notify mechanism. The ReentrantLock class can of course also be implemented, but it needs the help of the Condition interface and the newCondition() method.

If you think this article is helpful to you, you can like it and follow it to support it, or you can follow my public account, there are more technical dry goods articles and related information sharing, everyone can learn and progress together!


 

Guess you like

Origin blog.csdn.net/weixin_48182198/article/details/108709120