ReentrantLock example and principle (Synchronized difference)

AQS series

1. AQS core principle
2. ReentrantLock principle and example
3. CountDownLatch / Semaphore example and usage scenario
4. BlockingQueue example and usage scenario

1. The principle of ReentrantLock

ReentrantLock is based on the implementation of the AQS framework application. It is a means of thread concurrency control provided by JDK1.5 . It is similar to the function of the synchronized keyword. It is a mutex that can ensure thread security.
Then when we instantiate it, we can find that the construction method ReentrantLock (boolean fair) has a fair parameter, and the parameter is true for a fair lock, and false for an unfair lock.
And ReentrantLock is a reentrant lock, which means that we can call the lock() method multiple times to lock repeatedly. Of course, in this case, we need to call the unlock() method multiple times to unlock it multiple times.

ReentrantLock is a kind of mutual exclusion lock, so how to realize fair lock and unfair lock? Inside ReentrantLock, a Sync
internal class is defined , which inherits AbstractQueuedSynchronizer and implements some methods, which is also the implementation of template mode. It also defines the FairSync (fair lock) and NonfairSync (unfair lock) classes, both of which are inherited from Sync and indirectly based on AbstractQueuedSynchronizer , so ReentrantLock also implements fair locks and unfair locks . Then ReentrantLock itself implements the Lock interface to realize waiting and waking up.

2. Going to the public toilet (Demo)

Here is a small demo to use the ReentrantLock locking function. Many of them are examples of selling tickets. Multiple windows or multiple interfaces sell one ticket, but they cannot be sold repeatedly. Then I will change the scene here, a public toilet Multiple doors that can only be used by one person at a time.
The scene is like this, there can be multiple roads leading to the bathroom in the mall, but only one person can use it at a time, unless someone else agrees to use it with you ^ _ ^, probably no one will.
Nowadays, there are all qualified people. If there are many people who come at a certain time and see someone, they will line up. This is a fair lock.

2.1 Fair lock

public class ReentrantLockTest {
    
    
    //表示排到第几个人了
    volatile static int index = 0;
    //卫生间门上有个锁,人民素质高: true, 素质低: false
    static ReentrantLock lock = new ReentrantLock(true);

    public static void main(String[] args) {
    
    
        start();
    }

    /**
     * 开始活动
     */
    private static void start(){
    
    
        //下面实例化来卫生间的路线,有3个路线可以过来
        new Thread(() -> {
    
    
            for(;;){
    
    
                doing(index++);
            }
        }).start();
        new Thread(() -> {
    
    
            for(;;){
    
    
                doing(index++);
            }
        }).start();
        new Thread(() -> {
    
    
            for(;;){
    
    
                doing(index++);
            }
        }).start();
    }

    /**
     * 上厕所这个动作
     */
    private static void doing(int person){
    
    
        lock.lock();
        System.out.println("第 " + person + " 个人去了" + Thread.currentThread().getName() + " 坑位!");
        try {
    
    
            System.out.println("上卫生间中,顺便抽烟!");
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("第 " + person + " 个人完事了!");
        System.out.println("----------------------------");
        lock.unlock();
    }
}

The execution results are as follows:

0 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!0 个人完事了!
----------------------------1 个人去了Thread-1 坑位!
上卫生间中,顺便抽烟!1 个人完事了!
----------------------------2 个人去了Thread-2 坑位!
上卫生间中,顺便抽烟!2 个人完事了!
----------------------------3 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!3 个人完事了!
----------------------------4 个人去了Thread-1 坑位!
上卫生间中,顺便抽烟!4 个人完事了!
----------------------------5 个人去了Thread-2 坑位!
上卫生间中,顺便抽烟!

It can be seen from the execution results that Thread-0, Thread-1, and Thread-2 are executed alternately, very regularly, each time they are behind the previous person, 1 2 3 4 5 in order, this is a fair lock.

2.2 Unfair lock

Let's change the parameter of instantiating ReentrantLock to false to see.

static ReentrantLock lock = new ReentrantLock(false);

The execution results are as follows:

0 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!0 个人完事了!
----------------------------3 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!3 个人完事了!
----------------------------4 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!4 个人完事了!
----------------------------5 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!5 个人完事了!
----------------------------6 个人去了Thread-0 坑位!
上卫生间中,顺便抽烟!

It can be seen from the execution results that the first person and the second person did not grab the pit, and the 4th, 5th, and 6th people also came from the Thread-0 route, so the people from Thread-1 and Thread-2 basically "suffocated". up".

3. The difference between ReentrantLock and Synchronized

difference:

ReentrantLock Synchronized
ReentrantLock is a class that implements locking Synchronized is the internal implementation of JDK, and the keyword is locked
ReentrantLock is to use tryLock() to try to acquire locks, avoid deadlocks, and be safer Synchronized will have a deadlock problem
ReentrantLock needs to consider exceptions and release the lock in finally{} Synchronized does not need to consider abnormal conditions
ReentrantLock provides the lock and unlock function by itself. Locking and unlocking under special conditions requires the help of the Condition object. Synchronized needs the wait() provided by Object, and the notify() method realizes locking and unlocking under special conditions
ReentrantLock has a finer granularity and is more convenient to use Synchronized can only lock on methods or code blocks

Same point:

  1. Both ReentrantLock and Synchronized allow a thread to acquire the same lock multiple times.
  2. They are locked synchronously.

Four. Summary

In fact, after the current JDK8, Synchronized has been optimized. When the concurrency is not particularly high, it is almost the same as ReentrantLock. Synchronized starts from a lock-free state, becomes a biased lock as the number of threads increases, upgrades to a lightweight lock, and finally upgrades to a heavyweight lock. However, general e-commerce projects or advertising projects, express delivery, etc. are all high-concurrency projects. Under such circumstances, a single application will definitely not work. However, in general scenarios, it is still necessary to use ReentrantLock to be more flexible and convenient. .

5. Synchronized (interview extension)

As mentioned earlier, the Synchronized keyword is locked, but the Synchronized keyword can be used alone as a code block, or it can be added to a method. Adding it to the code block locks this piece of code, so add it to the method to lock it what is it then?

We know that the Synchronized synchronization code block is only executed by one thread at a time, let's try it with a small demo.
Below is a static method mF() that prints 5 times, starting and ending with start and end.

public static void mF(){
    
    
    System.out.println("start...");
    synchronized (Object.class){
    
    
        for(int i=0; i<=5; i++){
    
    
            System.out.println("method F!" + Thread.currentThread().getName());
            try {
    
    
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    System.out.println("end...");
}

Call the mF() method with two threads.

new Thread(() -> {
    
    
            SynchronizedTest.mF();
        }).start();
        new Thread(() -> {
    
    
            SynchronizedTest.mF();
        }).start();

Results of the:

start...
start...
method F!Thread-0
method F!Thread-0
method F!Thread-0
method F!Thread-0
method F!Thread-0
method F!Thread-0
end...
method F!Thread-1
method F!Thread-1
method F!Thread-1
method F!Thread-1
method F!Thread-1
method F!Thread-1
end...

It can be found that two "start..." are printed, Thread-0 is executed, and Thread-1 is executed.

If it is the Synchronized keyword loading method, we know that a class can have static methods and instance methods , so what is the difference between these two methods?
Answer : Instance locks and class locks do not interfere with each other ! Write an example to see.

public synchronized static void mA(){
    
    
    for(;;){
    
    
        System.out.println("method A!");
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

public static synchronized void mB(){
    
    
    for(;;){
    
    
        System.out.println("method B!");
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

public synchronized void mC(){
    
    
    for(;;){
    
    
        System.out.println("method C!");
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

public synchronized void mD(){
    
    
    for(;;){
    
    
        System.out.println("method D!");
        try {
    
    
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

Results of the:

method A!
method D!
method C!
method C!
method D!
method A!
method D!
method A!
method C!

It can be seen from the execution results that method ADC has been executed, but B has not been executed. The summary is as follows:
methods A and B are both static methods, and the static method is locked with the Synchronized keyword. The method is the same lock, so only There is a method to execute, and methods C and D are both instance methods , which do not interfere with static method A , so they are both being executed. Why can another instance method be notified of execution? Because the instance method is locked with the Synchronized keyword, and the lock is the this object, it will be executed.

Guess you like

Origin blog.csdn.net/qq_19283249/article/details/128512367