JAVA development (the use and difference of synchronized, lock, volatile)

In Java development, there are many scenarios for using locks. Sometimes we must ensure the atomic operation of a certain business, so locks may be needed. The so-called lock is to exclusively share a piece of memory and CPU computing space. In java code, there are several keywords related to the use of locks: synchronized, lock, and volatile. In this section, we will sort out the use and differences of these methods.

synchronized:

The synchronized keyword solves the synchronization of resource access between multiple threads. The synchronized translation into Chinese means synchronization, also known as “synchronization lock”.
The role of synchronized is to ensure that at the same time, only one thread will execute the modified code block or method, so as to achieve the effect of ensuring concurrency safety.

There are three ways to use synchronized: code blocks, member methods, and static methods

1. Act on the code block:

 synchronized(this) {
if(null == this. searchByOrigPhone(encryPhone)) {
//There is the same openId, delete the original information first
this.deleteByOpenId(mUser.getOpenId());
this.save(mUser.encryptSelf());
};
}

2. Act on member methods:

public synchronized void method(){
// code
}

3. Act on static methods:

public static synchronized void method(){
    // code
}

Synchronized Features:

Atomicity: Access synchronization code that ensures mutual exclusion between threads. Synchronized guarantees that only one thread gets the lock and enters the synchronized code block to operate shared resources, so it is atomic.

Visibility: Ensure that the modification of shared variables can be seen in time. When executing synchronized, the corresponding lock and unlock atomic operations will be executed. The lock operation will clear the value of the variable in the workspace; before performing the unlock operation, the variable must be synchronized back to the main memory.

Orderliness: The code inside synchronized and the external code are prohibited from sorting. As for the internal code, sorting is not prohibited, but since only one thread enters the synchronization code block, it is equivalent to a single thread in the synchronization code block. According to as -if-serial semantics, even if reordering occurs within the code block, it will not affect the result of program execution.

Pessimistic lock: synchronized is a pessimistic lock. Every time a shared resource is used, it is considered that there will be competition with other threads, so the shared resource will be locked every time it is used.

Exclusive lock (exclusive lock): synchronized is an exclusive lock (exclusive lock). The lock can only be held by one thread at a time, other threads are blocked.

Unfair lock: synchronized is an unfair lock. The order in which threads acquire locks may not follow the blocking order of threads. Allows the thread to attempt to acquire the lock immediately after making the request.

Reentrant lock: synchronized is a reentrant lock. The lock-holding thread can acquire its own internal lock again.

Lock:

Lock is an interface, and an implementation class is ReentrantLock. Lock is a thread synchronization tool introduced in java 1.5, which is mainly used for the control of shared resources under multi-threading.

Interface source code:

public interface Lock {

    /**
     * Acquire the lock, the thread that calls this method will acquire the lock, when the lock is acquired, it will use this method but will
     */
    void lock();

    /**
     * Can respond to interrupt. That is, the current thread can be interrupted during the process of acquiring the lock
     */
    void lockInterruptibly() throws InterruptedException;

    /**
     * Try to acquire a lock in a non-blocking manner. It will return immediately after calling this method. If the lock is acquired, it will return true otherwise it will return false
     */
    boolean tryLock();

    /**
     * Timed out acquisition lock, the following three situations will return
     * ①The current thread has acquired the lock within the timeout period
     * ②The current thread is interrupted within the timeout period
     * ③ When the timeout expires, return false
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /**
     * release lock
     */
    void unlock();

    /**
     * Obtain the waiting notification component, which is bound to the current lock. The current thread can only call the wait method of the component when it acquires the lock. After calling this method, the lock will be released
     */
    Condition newCondition();
}

The use of Lock:

Lock lock = new ReentrantLock();
lock. lock();
try {
    //processing tasks
}catch(Exception ex){
     
}finally{
    lock.unlock(); //release the lock
}

volatile

Volatile is a lightweight synchronization mechanism provided by Java. Unlike synchronized modification methods and code blocks, volatile is only used to modify variables. And unlike heavyweight locks such as synchronized and ReentrantLock, volatile is lighter because it does not cause thread context switching and scheduling.

The use of volatile, take a single case as an example:

public class Singleton {
    // Modified with volatile, after assignment, other threads can immediately perceive
    private static volatile Singleton instance;
 
    private Singleton() {
    }
 
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton. class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
 
}

The relationship and difference of various synchronization mechanisms:
Rationale:
The synchronized keyword belongs to the JVM level and is implemented through the monitorenter and monitorexit instructions. The bottom layer is completed through the monitor object. In fact, methods such as wait notify also rely on the monitor object. Only synchronization blocks or synchronization methods can call wait, notify and other methods;
Lock is a concrete class (java.utils.concurrent.locks.Lock) is a lock at the API level
How to use:
Synchronized does not allow explicit manual acquisition and release of locks, the system will automatically let the thread release the occupation of the lock
ReectrantLock: It is necessary to explicitly acquire and release the lock. If the lock is not released correctly, it may lead to deadlock;
Whether the wait can be interrupted
synchronized cannot be interrupted unless an exception is thrown or normal operation completes
ReentrantLock can be interrupted, 1. Set the timeout tryLock (timeout), 2.lockInterruptibly placed in the code block, call the interrupter() method to interrupt
Is the lock fair:
synchronized is an unfair lock
ReentrantLock can be both, the default is an unfair lock, the construction method passes in a boolean value, true is a fair lock, false is
ReentrantLock: The threads that need to be awakened to achieve group wakeup can achieve precise wakeup, instead of either randomly waking up a thread or waking up all threads like synchronized;

Unlike synchronized modification methods and code blocks, volatile is only used to modify variables. And unlike heavyweight locks such as synchronized and ReentrantLock, volatile is lighter because it does not cause thread context switching and scheduling.

According to the nature of the lock, the lock can be divided into the following categories:

Pessimistic lock or optimistic lock: whether to lock
Shared lock or exclusive lock (exclusive lock): whether multiple threads can hold the lock at the same time
Fair lock or unfair lock: whether to take locks in blocking order
Reentrant lock or non-reentrant lock: whether the thread holding the lock can hold the lock multiple times

———————————————– ————–

Optimistic lock:

As the name suggests, it is very optimistic. Every time I go to get the data, I think that others will not modify it, so it will not be locked, but when updating, I will judge whether others have updated the data during this period, and I can use the version number, etc. mechanism.

Pessimistic lock:

Always assume the worst case, every time you go to get the data, you think that others will modify it, so every time you get the data, you will lock it, so that others will block until they get the lock if they want to get the data.

Many such locking mechanisms are used in traditional MySQL relational databases, such as row locks, table locks, read locks, write locks, etc., which are all locked before operations.

Shared lock:

A shared lock refers to sharing the same lock on the same resource for multiple different transactions. It is equivalent to having multiple keys for the same door. Just like this, your house has a gate, and there are several keys to the gate. You have one, and your girlfriend has one. Both of you may enter your house through this key. This is the so-called shared lock.

Fair lock:

It is very fair. In a concurrent environment, each thread will first check the waiting queue maintained by the lock when acquiring a lock. If it is empty, or the current thread is the first in the waiting queue, it will occupy the lock, otherwise it will be added to the In the waiting queue, it will be taken from the queue according to the rules of FIFO in the future.

Unfair lock:

When you come up, try to occupy the lock directly. If the attempt fails, you will use a method similar to a fair lock.

The advantage of unfair lock is that it can reduce the overhead of evoking threads, and the overall throughput efficiency is high, because threads have a chance to obtain locks directly without blocking, and the CPU does not have to wake up all threads. The disadvantage is that threads in the waiting queue may starve to death, or wait a long time to acquire the lock.

Reentrant lock:

When a thread requests a lock held by other threads, the thread will be blocked (the subsequent read-write lock is not considered), but built-in locks like synchronized are reentrant, that is, a A thread attempts to acquire a lock already held by that thread, and the request will succeed. Reentrancy thinks that the operation granularity of this lock is at the thread level rather than at the call level. The ReentrantLock we mentioned below is also reentrant, and in addition to supporting reentrant locks, the synchronization component also supports fair and unfair selection.