The most complete history of Java introduces various locks

More exciting original content, please visit: JavaInterview , welcomed the star, support and encourage the authors are extremely grateful.

Classification lock Introduction

Optimistic and pessimistic locking lock

A Macroscopic classification lock is optimistic locking and pessimistic locking . Optimistic locking and pessimistic locking means which are not specific lock (in Java did not realize that the specific name of the lock is called optimistic locking or pessimistic locking), but in the case of concurrent two different strategies.

Optimistic locking (Optimistic Lock) is very optimistic, pick up data every time that other people are not modified. So it will not be locked. But if you want to update the data, it will be in check this time reading to update others have not changed the data before updating . If modified, then re-read, try to update again, the above steps until the update cycle of success (of course, allow the thread to give update failed update operation).

Pessimistic locking (Pessimistic Lock) is very pessimistic, pick up data every time you think others will be modified. So every time locked in time to get the data. So that others take the time data will be blocked until the lock is released pessimistic, want to get the thread data again to acquire the lock, and then get the data.

Blocking transaction pessimistic locking, optimistic locking roll back retry , they have two advantages and disadvantages, not good or bad, only the difference to adapt to different scenarios. For example: optimistic lock for the case of a write less, that is really rare scene of conflict, so can save lock overhead, increase the overall throughput of the system. However, if the frequent conflict, the upper application will continue to retry, but this reduces the performance, so this scenario pessimistic locking more appropriate. Summary: optimistic locking for write less, the scene of conflict rarely occurs; and write more and more scenes of conflict for use pessimistic locking .

Optimistic locking base --- CAS

In the implementation optimistic locking, we need to understand a concept: CAS.

What is CAS it? Compare-and-Swap, i.e. comparison and replacement , or compare and set .

  • Comparison: reading to a value A, before it is updated to B, to check whether the original value of A (not modified by other threads, here ignored the ABA problem ).

  • Alternatively: If yes, update A is B, ending. If not, it will not be updated.

The above two steps are atomic operations, can be understood as instantaneous, it seems that one step in the CPU.

With CAS, you can achieve an optimistic lock:


public class OptimisticLockSample{
    
    public void test(){
        int data = 123; // 共享数据
        
        // 更新数据的线程会进行如下操作
        for (;;) {
            int oldData = data;
            int newData = doSomething(oldData);
            
            // 下面是模拟 CAS 更新操作,尝试更新 data 的值
            if (data == oldData) { // compare
                data = newData; // swap
                break; // finish
            } else {
                // 什么都不做,循环重试
            }
        }   
    }
    
    /**
    * 
    * 很明显,test() 里面的代码根本不是原子性的,只是展示了下 CAS 的流程。
    * 因为真正的 CAS 利用了 CPU 指令。
    *  
    * */ 
    

}

复制代码

In Java native method is achieved by CAS.


public final class Unsafe {
    
    ...
    
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);  
    
    ...
} 


复制代码

And wrote a simple intuitive optimistic locking (it should be the exact optimistic locking process) implementation that allows multiple threads simultaneously read (because there is no locking operation), if the update data, then, and only one thread can update the data successfully, and lead to other threads need to roll back and try again. CAS instruction by the CPU, from the hardware level to ensure the atomicity, of the lock in order to achieve similar effects.

As it can be seen from the entire process optimistic locking, and did not lock and unlock operations, and therefore optimistic locking strategy is also known as lock-free programming . In other words, optimistic locking in fact not "lock", it is just a cycle retry algorithm CAS only.

Spinlocks

synchronized 与 Lock interface

Java locked in two implementations: one is to use the synchronized keyword, the other is a class that implements the Lock interface.

See in an article a good contrast, very image, synchronized keyword like automatic transmission , to meet all driving needs. But if you want to do more advanced operations, such as drift or play a variety of advanced Sao operation, you need a manual transmission , which is the implementation class Lock interface.

The synchronized after various Java each version optimized efficiency becomes high. Lock did not just use interface implementation class so convenient.

synchronized lock escalation process is the optimization of its core: biased locking -> Lightweight lock -> heavyweight lock


class Test{
    private static final Object object = new Object(); 
    
    public void test(){
        synchronized(object) {
            // do something        
        }   
    }
    
}

复制代码

Using the synchronized keyword lock a block of code when a start lock objects (that is, the above code object) is not a heavyweight lock , but tend to lock. Biased locking literal meaning is "biased in favor of the first thread to get it," the lock. After completion of the implementation of thread synchronization code block, and will not take the initiative to release the biased locking . When the second sync block reaches the thread determines whether the thread holding the lock case is their (thread holding the lock in the object ID stored in advance), if the normal execution down. Because not previously released, there is no need to re-lock , if a thread from start to finish using locks, obviously biased locking is almost no overhead, extremely high performance.

Once the second thread to join lock contention , biased locking converted to lightweight lock ( spin locks ). Lock competition: If multiple threads in turn acquire a lock, but every time you get a very smooth, no blocking occurs, then there is no lock contention. Only when a thread acquires the lock and found that the lock has been occupied, need to wait for its release, it shows that there is lock contention.

Lightweight lock on the state to continue to lock contention, did not grab the lock thread spinning operation, that is kept in a loop to determine whether locks can be acquired. The operation to acquire the lock, the lock flag is through the advance of the CAS operation to modify the object. To compare the current lock flag whether the released state, if it is, it is set to lock status, compare and settings are atomic operations, this is the JVM level guaranteed. Even if the current thread holds the lock, then thread the current lock holder information changed themselves.

If we get to the lock-threaded operating for a long time, such as would be complicated calculations, large amounts of data network transmission; then the other threads waiting for the lock goes into a long spin operation, this process is very resource-intensive of. In fact, this time the equivalent of only one thread work effectively, other threads can not do anything in vain consumption CPU, a phenomenon called busy wait (BUSY-Waiting) . So if multiple threads use an exclusive lock , but no lock contention happen, or happened very slight lock contention, then the synchronized lock is lightweight, allowing busy for a short time and so on. This is a selection of ideas for a short time and so busy, in exchange for spending thread between user mode and kernel mode switching .

Obviously, busy, etc. is limited (JVM has a counter records the number of spins, 10 cycles allowed by default, you can change the virtual machine parameters ). If the lock serious competition, reaching a maximum spin-count of threads, locks will be upgraded to lightweight heavyweight lock (CAS still by modifying the lock flag, but does not modify the thread that holds the lock ID). When the subsequent thread tries to acquire the lock, the lock is found to be occupied heavyweight locks, directly to hang himself (rather than above said busy, etc., that will not spin), waiting for the release of the lock thread to wake up. Before JDK1.6, synchronized direct plus heavyweight lock, it is clear now after a series of optimization, performance has improved significantly.

The JVM, only in accordance with the synchronized biased locking latch, lock sequence lightweight, heavyweight lock escalated (this is also called the lock inflation procedure), allowed degraded.

Reentrant lock (lock recursion)

Reentrant lock literally means "to re-enter the lock" that allows to obtain the same lock many times the same thread . For example, a recursive function, there are locking operation, recursive functions in this lock will block myself? If not, then this lock is called reentrant lock (for this reason reentrant lock also called recursive lock ).

Java to name begins with Reentrant locks are reentrant locks, and all available Lock implementation class provided by the JDK, including the synchronized keyword lock is reentrant . If you really need not reentrant lock, then you need to realize their own, and get to go online to search, there are a lot that he is also very simple to implement.

If the meaning is not reentrant lock in a recursive function will cause deadlocks, lock it in Java are basically reentrant lock, non-reentrant lock is not great, I do not think what the scene will be used ; Note: there is need to think of non-reentrant lock scene little friends can discuss with the message .

The following figure shows what the associated implementation class Lock:

ilock.png

Fair and unfair lock lock

If multiple threads to apply a fair lock , the thread releases the lock when the lock is obtained, the first application to get, very fair. If it is unfair lock , the thread after the application is likely to first obtain a lock, random or otherwise obtain, are based algorithm dependent.

For ReentrantLock class, a constructor can specify whether the lock latch is fair, fair non-default lock . Because in most cases, non-lock fair throughput is greater than the fair locks, if no special requirements, give priority to the use of unfair lock.

For synchronized lock is concerned, it can only be an unfair lock, there is no way that it becomes fair locks. This is also ReentrantLock One advantage synchronized lock, and more flexible.

The following code is configured ReentrantLock:


/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

复制代码

Internal ReentrantLock realized FairSync and NonfairSync two internal classes to implement fair and unfair lock lock. Specific source code analysis will be given in the next section, please pay attention to the project, welcomed the fork and Star .

Interruptible lock

Literally means "to respond to interrupt the lock."

First, we need to understand what is interrupted . Java and does not provide any direct way to break the thread, provided only interruption mechanism . So what is interrupt mechanism it? A thread is issued to the thread B "Please stop running," the request is to call Thread.interrupt () method (of course thread B itself can give yourself an interrupt request that Thread.currentThread (). Interrupt ()) , but thread B does not stop running immediately, but their choice at the right point in time interrupt response in its own way, can safely ignore this interrupt. In other words, Java is interrupted can not directly terminate the thread , just set the interrupt status in response to the state of the interrupted thread needs to decide how to deal with. It's like at the time of reading, the teacher told the students in the study up their own homework, but whether students review homework, homework depends entirely on how the students themselves.

Analysis back to lock up, if thread A holds a lock, hold the thread B waiting to acquire the lock. Since A thread holding a lock for too long, thread B do not want to wait, we can make their own thread B interrupt or interrupt other threads inside B, which is to be the middle of the lock .

In Java, synchronized lock is not interrupted locks , and Lock implementation classes are interruptible lock . Which can be seen JDK Lock locked themselves to achieve more flexible, which is synchronized with the lock, why should realize Lock always such implementation class.

Lock interface definitions:


public interface Lock {

    void lock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();
    
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    
    void unlock();

    Condition newCondition();
}

复制代码

Which is to get lockInterruptibly interruptible lock.

Shared lock

Literally it means multiple threads can share a single lock. Shared locks are generally used when reading data, such as we can allow 10 threads simultaneously reading a shared data, this time we can set up a shared lock 10 voucher.

In Java, there are specific implementation class shared locks, such as Semaphore. This class source code analysis will be analyzed in later chapters, so stay tuned to the project, welcomed the fork and Star .

Mutex

Literally means between the thread mutually exclusive lock, the lock is shown that only one thread of ownership.

In Java, ReentrantLock, synchronized locks are mutex.

Read-Write Lock

Read-write lock is actually a lock, a read lock (shared lock) and a write lock (exclusive lock, exclusive lock).

In Java, ReadWriteLock interfaces only provides two methods to return a read lock, write lock a return.


public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

复制代码

Previous article talked about [optimistic locking strategy] (# optimistic locking base --- CAS), all threads can be read at any time, just before writing the judgment values ​​have not been changed.

In fact, read-write lock to do the same, but slightly different strategy. In many cases, the thread know after reading the data, whether to change it. So why not lock when directly clear this? If I read value to update it (SQL for update of what it means), then the lock when added directly to a write lock , when I hold a write lock, other threads either read or write need to wait; if read the data just to show the front, then a definite plus when locking read lock , other threads must increase if a read lock, no need to wait, you can get directly (read lock counter is incremented by 1).

Although the read-write lock and optimistic feeling a bit like a lock, but the read-write lock is pessimistic locking strategy . Because the read-write lock is not in the update before judging value has not been modified, but before locking the decision should read lock or write lock. Especially optimistic locking lock-free programming.

Internal JDK provides a unique interface class is a ReadWriteLock ReentrantReadWriteLock. The lock can be seen by name provides a write lock, and the lock is reentrant.

to sum up

Java used in a variety of locks are basically pessimistic locking , optimistic locking it in Java have it? The result is positive, that is java.util.concurrent.atomic following atomic classes are optimistic locking is achieved by the. as follows:


public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

复制代码

Can be found via the source, we continue to CAS in a loop inside, until it succeeds.

Parameter Description

-XX:-UseBiasedLocking=false 关闭偏向锁

JDK1.6 

-XX:+UseSpinning 开启自旋锁

-XX:PreBlockSpin=10 设置自旋次数 

JDK1.7 之后 去掉此参数,由 JVM 控制


复制代码

Guess you like

Origin juejin.im/post/5dc437a9f265da4d144e873a