The implementation principle and difference between synchronized and lock [detailed]

Synchronized implementation principle

Every object in java can be used as a lock, which is the basis for synchronized synchronization:

  • Ordinary synchronization method, the lock is the current instance object
  • Static synchronization method, the lock is the class object of the current class
  • Synchronized method block, the lock is the object in the brackets
  • When a thread accesses a synchronized code block, it first needs to obtain a lock, and must release the lock when exiting or throwing an exception, so how does it implement this mechanism? Let's look at a simple piece of code first:
package cn.alibab.javap;

public class SynchronizedTest {
    
    

    public synchronized void test1(){
    
    

    }
    public void test2(){
    
    
        synchronized (this){
    
    

        }
    }
}

Use the javap tool (javap is a decomposer for class files after java compilation) to view the generated class file information to analyze the implementation of Synchronized. From the above, it can be seen that the Please add a picture description
Please add a picture description
synchronization code block is implemented using the monitorenter and monitorexit instructions, and the synchronization method (here If you can’t see it, you need to look at the underlying implementation of the JVM) it relies on the ACC_SYNCHRONIZED implementation on the method modifier.

Synchronized code block:

The monitorenter instruction is inserted into the start position of the synchronization code block after compilation, and the monitorexit instruction is inserted into the end position of the synchronization code block. The JVM needs to ensure that each monitorenter has a monitorexit corresponding to it. Any object has a monitor associated with it, and when a monitor is held, it will be locked. When the thread executes the monitorenter instruction, it will try to acquire the ownership of the monitor corresponding to the object, that is, try to acquire the lock of the object; [from the art of concurrent programming]

Synchronization method:

The synchronized method will be translated into ordinary method call and return instructions such as: invokevirtual, areturn instructions, and there is no special instruction at the VM bytecode level to implement the method modified by synchronized, but in the method table of the Class file The synchronized flag in the access_flags field of the method is set to 1, indicating that the method is a synchronized method, and the object that calls the method or the Class to which the method belongs is represented by the internal object Klass in the JVM as the lock object. (From: http://www.cnblogs.com/javaminer/p/3889023.html)

Please add a picture description

The difference between synchronized and lock:

source

  • lock is an interface, and synchronized is a keyword of java, and synchronized is a built-in language implementation;

Whether the exception releases the lock

Synchronized will automatically release the occupied lock when an exception occurs, so there will be no deadlock; when the lock occurs abnormally, it will not actively release the occupied lock, and must be manually unlocked to release the lock, which may cause deadlock (so it is best Wrap the synchronous code block with try catch and write unlock in finally to avoid deadlock.)

Whether to respond to interrupts

The lock can use interrupt to interrupt the wait while waiting for the lock, while synchronized can only wait for the release of the lock and cannot respond to interrupts;

Do you know to acquire the lock

Lock can use trylock to know whether it has acquired a lock, but synchronized cannot;

efficiency

Lock can improve the efficiency of multiple threads for read operations. (readwritelock can be used to achieve read-write separation)

performance

In terms of performance, if the competition for resources is not intense, the performance of the two is similar. When the competition for resources is very intense (that is, there are a large number of threads competing at the same time), the performance of Lock is far better than synchronized. Therefore, it should be selected according to the appropriate situation in specific use.

scheduling mechanism

Synchronized uses the wait, notify, and notifyAll scheduling mechanisms of the Object object itself, while Lock can use Condition to schedule between threads.

//Condition定义了等待/通知两种类型的方法
Lock lock=new ReentrantLock();
Condition condition=lock.newCondition();
...
condition.await();
...
condition.signal();
condition.signalAll();

The usage difference between synchronized and lock

synchronized:

Add this control to the object that needs to be synchronized. Synchronized can be added to the method or in a specific code block. The object that needs to be locked is indicated in parentheses.

lock:

Generally use the ReentrantLock class as a lock. Locking and unlocking need to be indicated through lock() and unlock(). Therefore, unlock() is generally written in the finally block to prevent deadlock.

The performance difference between synchronized and lock

Synchronized is entrusted to the JVM for execution,
and lock is the code written by java to control the lock.

In Java1.5, synchronize is performance inefficient. Because this is a heavyweight operation that needs to call the operation interface, it is possible that locking may consume more system time than operations other than locking. In contrast, using the Lock object provided by Java has higher performance.

But by Java1.6, a change has taken place. Synchronize is semantically clear and can be optimized a lot, including adaptive spin, lock elimination, lock coarsening, lightweight locks, biased locks, and more. As a result, the performance of synchronize on Java1.6 is not worse than that of Lock. The official also stated that they also support synchronize more, and there is still room for optimization in future versions.

The specific difference between the two mechanisms:
synchronized originally uses the CPU pessimistic lock mechanism, that is, the thread obtains an exclusive lock. An exclusive lock means that other threads can only rely on blocking to wait for the thread to release the lock. When the CPU conversion thread is blocked, it will cause thread context switching. When there are many threads competing for locks, it will cause frequent CPU context switching, resulting in low efficiency.

And Lock uses optimistic locking. The so-called optimistic locking is to complete an operation without locking every time but assuming that there is no conflict. If it fails due to conflict, try again until it succeeds. The mechanism for implementing optimistic locking is the CAS operation (Compare and Swap). We can further study the source code of ReentrantLock, and we will find that one of the more important methods for obtaining locks is compareAndSetState. This is actually a special instruction provided by the calling CPU.

Modern CPUs provide instructions that can automatically update shared data and can detect interference from other threads, and compareAndSet() uses these instead of locking. This algorithm is called a non-blocking algorithm, meaning that the failure or suspension of one thread should not affect the failure or suspension of other threads.

3. The difference between synchronized and lock purposes
There is no difference between synchronized primitives and ReentrantLock in general, but in very complex synchronization applications, please consider using ReentrantLock, especially when encountering the following two requirements.

1. A certain thread needs to be interrupted while waiting for the control of a lock.
2. It needs to handle some wait-notify separately. The Condition application in ReentrantLock can control which thread to notify.
3. It has a fair lock function, and each incoming Threads will all queue up

Let's talk about it in detail...

Let me talk about the first case first. There are two lock mechanisms of ReentrantLock, ignoring the interrupt lock and responding to the interrupt lock, which brings us a lot of flexibility. For example: if two threads A and B compete for the lock, thread A gets the lock, and thread B waits, but thread A really has too many things to deal with at this time, and if it does not return all the time, thread B may not be able to wait. I want to interrupt myself, stop waiting for this lock, and move on to other things. At this time, ReentrantLock provides two mechanisms: interruptible/non-interruptible.
First, the B thread interrupts itself (or other threads interrupt it), but ReentrantLock does not respond, and continues to let the B thread wait. When the ear winds (synchronized primitives are like this);
second, the B thread interrupts itself (or other threads interrupt it), ReentrantLock handles the interruption, and no longer waits for the arrival of the lock, giving up completely.

Guess you like

Origin blog.csdn.net/weixin_54046648/article/details/128172809