JUC - Multi-threaded pessimistic locks, optimistic locks, read-write locks (shared locks, exclusive locks), fair and unfair locks, reentrant locks, spin locks, deadlocks (10)

There are mainly the following locks in Java

1. Pessimistic lock, optimistic lock

Pessimistic lock : When the current thread operates data, it always thinks that other threads will modify the data, so it will be locked every time it operates data, and other threads will block when operating data, such as synchronized;

Optimistic lock : Every time the current thread operates data, it thinks that others will not modify it. When updating, it will judge whether others will update the data. It is judged by the version. If the data is modified, it will refuse to update. For example, cas is an optimistic lock. , but strictly speaking, it is not a lock. Atomicity is used to ensure data synchronization. For example, the optimistic lock of the database is implemented through version control. Cas does not guarantee thread synchronization. Optimistically, there is no other thread influence during data update

Summary: Pessimistic locks are suitable for scenarios with many write operations, optimistic locks are suitable for scenarios with many read operations, and the throughput of optimistic locks will be higher than that of pessimistic locks

2. Shared locks, exclusive locks (read-write locks, mutex locks) 

Read-write lock is a technology: implemented by the ReentrantReadWriteLock class

In order to improve performance, Java provides read-write locks. Read locks are used for reading, and write locks are used for writing. Flexible control. If there is no write lock, reading is non-blocking, which improves the program to a certain extent. execution efficiency.

Read-write locks are divided into read locks and write locks. Multiple read locks are not mutually exclusive, and read locks and write locks are mutually exclusive. This is controlled by the JVM itself.

Read lock (shared lock) : Allow multiple threads to acquire read locks and access the same resource at the same time

Write lock (exclusive) : Only one thread is allowed to acquire a write lock , and simultaneous access to the same resource is not allowed

Shared lock : also called read lock, which can view data, but cannot modify or delete a data lock. After locking, other users can read concurrently, but cannot modify, add, or delete data. This lock can be used by multiple threads Hold, for resource data sharing

Exclusive lock : also called exclusive lock, write lock, exclusive lock, exclusive lock, the lock can only be held by one thread at a time, after locking, any thread trying to lock again will be blocked until the current thread unlocks

For example: after thread A adds an exclusive lock to data, other threads can no longer add any type of lock to data, and the thread that acquires the mutex lock can both read and modify data

Read-write lock in Java: ReadWriteLock , ReadWriteLock implementation class ReentrantReadWriteLock

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock;其实现类 ReentrantReadWriteLock
 * 读-读  可以共存!
 * 读-写  不能共存!
 * 写-写  不能共存!
 */
public class ReadWriteLockTest {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        // 写入
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.put(temp + "",temp + "");
            },String.valueOf(i)).start();
        }

        // 读取
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCache.get(temp + "");
            },String.valueOf(i)).start();
        }
    }
}

class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();

    // 读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock lock = new ReentrantLock();

    // 存、写数据,只希望同时只有一个线程写
    public void put(String key,Object value){
        readWriteLock.writeLock().lock();

        try{
            System.out.println(Thread.currentThread().getName() + "写入" + key);

            map.put(key,value);

            System.out.println(Thread.currentThread().getName() + "写入OK");
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }

    // 取,读数据,所有线程都可以同时读取
    public void get(String key){
        readWriteLock.readLock().lock();

        try{
            System.out.println(Thread.currentThread().getName() + "读取" + key);

            map.get(key);

            System.out.println(Thread.currentThread().getName() + "读取OK");
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

output

1写入1
1写入OK
2写入2
2写入OK
3写入3
3写入OK
4写入4
4写入OK
5写入5
5写入OK
1读取1
2读取2
2读取OK
3读取3
1读取OK
3读取OK
5读取5
5读取OK
4读取4
4读取OK

1-5 When writing data, the execution order will not be preempted by other threads; but when reading data, it will jump in the queue 

3. Fair and unfair lock

Fair lock : There are multiple threads to acquire locks in the order in which they apply for locks, that is, if in a thread group, each thread can be guaranteed to get the lock, for example: ReentrantLock (synchronous queue FIFO used)

Unfair locks : The way to acquire locks is random. There is no guarantee that every thread can get the lock. Some threads will starve to death and never get the lock. For example: synchronized, ReentrantLock

Summary: The performance of unfair locks is higher than that of fair locks, and it can reuse CPU time

4. Reentrant lock (recursive lock)

Reentrant lock: also called recursive lock, after the outer layer uses the lock, the inner layer can still be used without deadlock

Non-reentrant lock: When the current thread executes a method and has acquired the lock, then when trying to acquire the lock again in the method, it will not be blocked

1. Explanation from Zhihu: Reentrant lock refers to the code that the inner recursive function can still acquire the lock after the outer function of the same thread acquires the lock. When the same thread acquires the lock in the outer method, in Entering the inner method will automatically acquire the lock. That is, a thread can enter any synchronized block of code that it already owns.

 2. Coffey's strong explanation: reentrant lock refers to the unit of thread. After a thread acquires the object lock, the thread can acquire the lock on the object again, while other threads cannot.

Both synchronized and ReentrantLock are reentrant locks

One of the meanings of reentrant locks is to prevent deadlocks

1、synchronized

/**
 * 可重入锁(递归锁)
 *      在外层使用锁之后,在内层仍然可以使用,并且不会产生死锁(前提是同一把锁,如同一个类、同一个实例、同一个代码块)
 *      1、来自知乎的解释:可重入锁指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个他已经拥有锁的所有同步代码块。
 *      2、Coffey强的解释:可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
 *      synchronized 和 ReentrantLock 都是可重入锁
 *      可重入锁的意义之一在于防止死锁
 * 不可重入锁:在当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞
 */
public class SynchronizedTest {
    public static void main(String[] args) {
        Object obj = new Object();

        new Thread(() -> {
            // 第一次加锁
            synchronized (obj){
                System.out.println(Thread.currentThread().getName() + "线程执行第一层");
                // 第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                synchronized (obj){
                    // 抛异常
                    int a = 10/0;
                    System.out.println(Thread.currentThread().getName() + "线程执行第二层");
                }
            }
        },"t1").start();

        new Thread(() -> {
            // 第一次加锁
            synchronized (obj){
                System.out.println(Thread.currentThread().getName() + "线程执行第一层");
                // 第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                synchronized (obj){
                    System.out.println(Thread.currentThread().getName() + "线程执行第二层");
                }
            }
        },"t2").start();
    }
}

There is an exception in the second layer of the t1 thread. Use the synchronized keyword. If an exception occurs, the lock will be released automatically, and the t2 thread will output normally. 

2、 ReentrantLock

/**
 * 可重入锁(递归锁)
 *      synchronized 和 ReentrantLock 都是可重入锁
 *      可重入锁的意义之一在于防止死锁
 */
public class ReentrantLockTest {
    public static void main(String[] args) {
        // 非公平锁
        Lock lock = new ReentrantLock(false);

        new Thread(() -> {
            // 第一次加锁
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + "线程执行第一层");

                // 第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                lock.lock();
                try {
                    // 抛异常
                    int a = 10/0;
                    System.out.println(Thread.currentThread().getName() + "线程执行第二层");
                } finally {
                    lock.unlock();
                }
            } finally { // Lock是显式锁,必须手动关闭锁(忘记关闭锁会导致 死锁)
                lock.unlock();
            }
        },"t1").start();

        new Thread(() -> {
            // 第一次加锁
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + "线程执行第一层");

                // 第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "线程执行第二层");
                } finally {
                    lock.unlock();
                }
            } finally { // Lock是显式锁,必须手动关闭锁(忘记关闭锁会导致 死锁)
                lock.unlock();
            }
        },"t2").start();
    }
}

There is an exception in the second layer of the t1 thread. Our release lock is written in finally, which does not affect the normal output of the t2 thread. 

Note:

1. Lock is an explicit lock, and the lock must be closed manually (opening and closing the lock manually, forgetting to close the lock will cause deadlock)          synchronized is an implicit lock, which will automatically release the lock, and it will automatically release when it is out of scope

2. Lock must be manually closed and released, and must be released in the finally clause

Source code comments in the ReentrantLock class

Five, spin lock

Spin lock: When a thread acquires a lock, if the lock has been acquired by other threads, then the thread will wait in a loop, and then continuously judge whether the lock can be successfully acquired, and will not exit the loop until the lock is acquired, at most at any time Only one execution unit can acquire the lock

Summary: There will be no thread state switching, and it will always be in the user state, which reduces the consumption of thread context switching. The disadvantage is that the loop will consume CPU

Six, heavyweight lock, lightweight lock

A heavyweight lock is a title: synchronized is implemented through a monitor lock (monitor) inside the object, and the monitor lock itself relies on the Mutex Lock of the underlying operating system to implement.

The operating system needs to switch from the user mode to the core mode to switch threads, which is very costly. This kind of lock that relies on the operating system Mutex Lock to implement is called a heavyweight lock. In order to optimize synchonized, lightweight locks and biased locks are introduced.

Heavyweight locks in Java: synchronized

Lightweight lock is a lock optimization mechanism added in JDK6: Lightweight lock uses CAS operation to eliminate the mutex used by synchronization without competition. Lightweight is relative to heavyweight locks implemented using operating system mutexes. Lightweight locks reduce the performance consumption of traditional heavyweight locks using operating system mutexes without multi-thread competition. If more than two threads compete for the same lock, the lightweight lock will not be effective and must be expanded to a heavyweight lock.

Advantages: If there is no competition, the overhead of using a mutex is successfully avoided by the CAS operation.

Disadvantages: If there is competition, in addition to the overhead of the mutex itself, additional CAS operation overhead is generated. Therefore, in the case of competition, lightweight locks are slower than traditional heavyweight locks.

Seven, bias lock

Biased lock is a lock optimization mechanism added in JDK6: in the case of no competition, the entire synchronization is eliminated, and even the CAS operation is not performed. Partiality refers to eccentricity, which means that the lock will be biased towards the first thread to obtain it. If the lock has not been acquired by other threads during the next execution process, the thread holding the biased lock will be locked forever. No further synchronization is required. Every time a thread holding a biased lock enters a synchronization block related to this lock, the virtual machine can no longer perform any synchronization operations (such as locking, unlocking, and updating operations on Mark Word, etc.).

Advantages: Eliminate the entire synchronization, even the CAS operation is not done, which is better than lightweight locks.

Disadvantages: If most of the locks in the program are always accessed by multiple different threads, biased locks are redundant.

Eight, segment lock

Segment lock is a mechanism: The best example to illustrate segment lock is ConcurrentHashMap. The principle of ConcurrentHashMap: It internally subdivides several small HashMaps, called segments (Segment). By default, a ConcurrentHashMap is further subdivided into 16 segments, which is the concurrency of locks. If you need to add a key-value to ConcurrentHashMap, instead of locking the entire HashMap, you first get the segment in which the key-value should be stored according to the hashcode, then lock the segment, and complete the put operation. In a multi-threaded environment, if multiple threads perform put operations at the same time, as long as the added key-value is not stored in the same segment, the threads can be truly parallel.

Thread safety: ConcurrentHashMap is a Segment array, and Segment is locked by inheriting ReentrantLock, so each operation that needs to be locked locks a segment, so as long as each Segment is thread-safe, global thread safety

Nine, mutex, synchronization lock

Mutex locks are synonymous with pessimistic locks and exclusive locks, which means that a resource can only be accessed by one thread and cannot be accessed by other threads.

read-read mutex

read-write mutex

write-read mutex

write-write mutex

Synchronization locks in Java: synchronized

A synchronization lock is synonymous with a mutex, which means that multiple threads are executed concurrently, and only one thread is allowed to access shared data at the same time.

Synchronization locks in Java: synchronized

10. Deadlock

Deadlock is a phenomenon: if thread A holds resource x, thread B holds resource y, thread A waits for thread B to release resource y, thread B waits for thread A to release resource x, neither thread releases its own resources, the two threads cannot obtain each other's resources, which will cause a deadlock.

The deadlock in Java cannot be broken by itself, so after the thread is deadlocked, the thread cannot respond. Therefore, we must pay attention to the concurrent scene of the program to avoid deadlock

Eleven, lock coarsening

Lock coarsening is an optimization technique: If a series of continuous operations repeatedly lock and unlock the same object, and even lock operations occur in the loop body, even if there is no thread competition, frequent Mutual exclusion synchronization operations will cause unnecessary performance loss, so a solution is adopted: expand (coarse) the scope of locking to the outside of the entire operation sequence, so that the frequency of locking and unlocking will be greatly reduced, thereby Reduced performance loss

12. Lock Elimination

Lock elimination is an optimization technique: it just kills the lock. Lock elimination can be performed when the Java virtual machine finds that some shared data will not be competed by threads when it is running.

Judging that shared data will not be competed by threads?

Use escape analysis technology: analyze the scope of the object. If the object is defined in method A and passed as a parameter to method B, it is called method escape; if it is accessed by other threads, it is called thread escape.

A certain data on the heap will not escape and be accessed by other threads, so it can be treated as data on the stack, and it is considered private to the thread, and synchronous locking is not needed

thirteen, synchronized

Synchronized is a keyword in Java: used to modify methods and object instances

synchronized belongs to exclusive lock, pessimistic lock, reentrant lock, unfair lock

1. When acting on an instance method, what is locked is the instance of the object (this);

2. When used as a static method, the Class class is locked, which is equivalent to a global lock of the class, and will lock all threads that call the method;

3. When synchronized acts on a non-NULL object instance, all code blocks that lock the object are locked. It has multiple queues. When multiple threads access an object monitor together, the object monitor will store these threads in different containers.

Each object has a monitor object. Locking is to compete for monitor objects . Code block locking is achieved by adding monitorenter and monitorexit instructions before and after the code block. Method locking is judged by a flag bit

Fourteen, the difference between Lock and synchronized

Lock: is an interface in Java, reentrant lock, pessimistic lock, exclusive lock, mutex lock, synchronization lock

1. Lock needs to manually acquire and release the lock. It's like the difference between automatic and manual

2. Lock is an interface, and synchronized is a keyword in Java, and synchronized is a built-in language implementation.

3. Synchronized will automatically release the lock occupied by the thread when an exception occurs, so it will not cause a deadlock phenomenon; while Lock, if an exception occurs, if it does not actively release the lock through unLock(), it is likely to cause a deadlock phenomenon , so you need to release the lock in the finally block when using Lock.

4. Lock can make the thread waiting for the lock respond to interruption, but synchronized cannot. When using synchronized, the waiting thread will wait forever and cannot respond to interruption.

5. Through Lock, you can know whether the lock has been successfully acquired, but synchronized cannot.

6. Lock can improve the efficiency of multiple threads for reading operations by implementing read-write locks.

Advantages of synchronized:

When only basic synchronization functions are needed, use synchronized

Lock should ensure that the lock is released in the finally block. If you use synchronized, the JVM ensures that even if an exception occurs, the lock is automatically released.

When using Lock, it is difficult for the Java virtual machine to know which lock objects are held by a specific thread lock

15. The difference between ReentrantLock and synchronized

ReentrantLock is a class in Java: inherits the Lock class, reentrant lock, pessimistic lock, exclusive lock, mutex lock, synchronization lock

Same point:

1. Mainly solve the problem of how to access shared variables safely

2. They are all reentrant locks, also called recursive locks. The same thread can acquire the same lock multiple times.

3. Guarantee the two characteristics of thread safety: visibility and atomicity

difference:

1. ReentrantLock is like a manual car, which needs to explicitly call the lock and unlock methods, and synchronized implicitly obtains the release lock.

2. ReentrantLock can respond to interrupts, and synchronized cannot respond to interrupts. ReentrantLock provides higher flexibility for dealing with lock unavailability

3. ReentrantLock is at the API level, and synchronized is at the JVM level

4. ReentrantLock can implement fair locks and unfair locks. The default is unfair locks. synchronized is an unfair lock and cannot be changed.

5. ReentrantLock can bind multiple conditions through Condition

Can refer to

Take you to a thorough understanding of 21 kinds of locks in Java

What are the locks in Java? _zhangjia_happy's Blog-CSDN Blog_What are the types of java locks?

Guess you like

Origin blog.csdn.net/MinggeQingchun/article/details/127383395