Understanding of reentrant locks in JAVA

Thanks to "StoneWang" for a deep soul summary of reentrant locks: Reentrant means that a thread has already acquired a certain lock and can acquire this lock again without deadlock.

1. Reentrant lock

  • synchronized
  • ReentrantLock

Two characteristics of reentrant locks:

  • When a thread A acquires the lock, then thread A can acquire the lock again without releasing the current lock
  • Other threads cannot obtain this lock. Only after this thread A releases the current lock, other threads can acquire this lock.

1.1 The characteristics of synchronized reentrant locks:

public class SynchronizedLockTest {
    
    
    //类锁,实际是锁类的class对象
    private static synchronized void synClass(){
    
    

        Thread thread = Thread.currentThread();
        System.out.println("当前执行synClass的线程名称:" + thread.getName());

        System.out.println("synClass going...");
            subSynClass();
        System.out.println("synClass end");
    }



    private static synchronized void subSynClass(){
    
    
        Thread thread = Thread.currentThread();
        System.out.println("当前执行 subSynClass 的线程名称:" + thread.getName());

        System.out.println("subSynClass going...");
        SleepTools.second(3);
        System.out.println("subSynClass end");
    }

    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("第一个线程开始执行");
                synClass();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("第二个线程开始执行");
                synClass();
            }
        });

        thread.setName("第一个线程");
        thread2.setName("第二个线程");

        thread.start();
        thread2.start();


    }
}

Output result:

第一个线程开始执行
第二个线程开始执行
当前执行synClass的线程名称:第一个线程
synClass going...
当前执行 subSynClass 的线程名称:第一个线程
subSynClass going...
subSynClass end
synClass end
当前执行synClass的线程名称:第二个线程
synClass going...
当前执行 subSynClass 的线程名称:第二个线程
subSynClass going...
subSynClass end
synClass end

You can see that the synchronized class lock is used in this example, which means that there is only one lock throughout this class.

  • The first feature of synchronized reentrancy is shown in: When the "first thread" executes the synchronized SynClass() method and gets the lock, then you can still call the synchronized subSynClass in SynClass() ()method.
  • The second feature of synchronized reentrancy: when the "second thread" calls synClass. Because the "first thread" has not released the lock, the "second thread" will block and wait.

Note: synchronized will release the lock when the method or code block is executed

1.2 Features of ReentrantLock reentrant lock:

public class ReentrantLockTest2 {
    
    
    static Lock lock = new ReentrantLock();


    public static void methodTest() {
    
    
        System.out.println(Thread.currentThread().getName() + "进入方法等待.....");
        try {
    
    
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "methodTest do something start。。。。");
             subMethodTest();
            System.out.println(Thread.currentThread().getName() + "methodTest do something end。。。。。");

        } finally {
    
    
            lock.unlock();
        }

    }


    public static void subMethodTest() {
    
    
        try {
    
    
            lock.lock();

            System.out.println(Thread.currentThread().getName() + "subMethodTest do something start");
            SleepTools.second(1);
            System.out.println(Thread.currentThread().getName() + "subMethodTest do something end");

        } finally {
    
    
            lock.unlock();
        }

    }


    public static void main(String[] args) {
    
    
        Thread thread = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                methodTest();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                methodTest();
            }
        });

        thread.setName("A线程");
        thread2.setName("B线程");

        thread.start();
        thread2.start();

    }
}

Output result

A线程进入方法等待.....
B线程进入方法等待.....
A线程methodTest do something start。。。。
A线程subMethodTest do something start
A线程subMethodTest do something end
A线程methodTest do something end。。。。。
B线程methodTest do something start。。。。
B线程subMethodTest do something start
B线程subMethodTest do something end
B线程methodTest do something end。。。。。
  • The first feature of ReentrantLock's reentrancy is shown in: A thread executes methodTest() to get the lock lock, and when subMethodTest() is called, it can still enter the code segment locked by the lock.
  • The second feature of synchronized reentrancy: When the B thread calls methodTest(), the code block locked by the lock cannot enter, and only waits outside.

The difference between ReentrantLock and synchronized is that you need to manually release the lock, so you must manually release the lock when using ReentrantLock, and the number of locks must be the same as the number of releases.

  • If the "lock.unlock();" in the methodTest() method of the code above is commented out, the print result is as follows:
B线程进入方法等待.....
A线程进入方法等待.....
B线程methodTest do something start。。。。
B线程subMethodTest do something start
B线程subMethodTest do something end
B线程methodTest do something end。。。。。

The B thread only releases the lock once, and the A thread will be blocked forever and will not get the chance to execute.

1.3 Implementation of reentrant lock

public class Lock{
    
    
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock()
            throws InterruptedException{
    
    
        Thread thread = Thread.currentThread();
        while(isLocked && lockedBy != thread){
    
    
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = thread;
    }
    public synchronized void unlock(){
    
    
        if(Thread.currentThread() == this.lockedBy){
    
    
            lockedCount--;
            if(lockedCount == 0){
    
    
                isLocked = false;
                notify();
            }
        }
    }
}

Logic when locking:

  1. When locking, get the current thread first. (Identify who needs the lock)
 Thread thread = Thread.currentThread();
  1. Judgment: When the critical resource has been locked, but the thread currently requesting the lock is not the thread that previously locked the critical resource. Then the thread currently requesting the lock needs to wait.
while(isLocked && lockedBy != thread){
    
    
        wait();
}

Two situations in which the lock can be obtained without waiting:

  • The current lock is not used by threads.
  • The current lock is used by a thread, and the thread currently requesting the lock is the thread currently using the lock.
  1. After getting the lock
isLocked = true; // 当前锁已经被使用了
lockedCount++;//锁的次数+1
lockedBy = thread;//记录拿到锁的线程

The thread currently requesting the lock first adds the lock, and then the number of locks + 1, and then assigns itself (this thread) to lockedBy to show who currently uses the lock to facilitate the while judgment when reentering.

Unlocking logic:

  1. First, see if the thread that requires unlocking is the thread currently using the lock. Not doing nothing. (Of course, you can’t let other threads execute the unlock code to unlock and use it at will. That’s equivalent to everyone having a key. The judgment here means that the unlock must be locked)
  2. Then reduce the number of locks by one.
  3. Then it is judged whether the number of locks has become 0.
  4. Change to 0 means that the lock has been completely unlocked. The locked flag islocked can be reset.
    And randomly wake up a thread waiting by wait(): notify()

2. Non-reentrant lock

Since the same thread can enter the same lock multiple times is called a reentrant lock, a non-reentrant lock means that the same thread cannot enter the same lock multiple times.

public class NotReentrantLock{
    
    
    private boolean isLocked = false;

    public synchronized void lock() {
    
    
        while(isLocked){
    
    
            try {
    
    
                wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        isLocked = true;
    }
    public synchronized void unlock(){
    
    
        isLocked = false;
        notify();
    }




    public static void main(String[] args) {
    
    
        NotReentrantLock lock = new NotReentrantLock();

        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                try {
    
    
                    lock.lock();
                    System.out.println("第1次获取锁,这个锁是:" + lock);

                    int index = 1;
                    while (true) {
    
    
                        try {
    
    
                            lock.lock();
                            System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);

                            try {
    
    
                                Thread.sleep(new Random().nextInt(200));
                            } catch (InterruptedException e) {
    
    
                                e.printStackTrace();
                            }

                            if (index == 10) {
    
    
                                break;
                            }
                        } finally {
    
    
                            lock.unlock();
                        }

                    }

                } finally {
    
    
                    lock.unlock();
                }
            }
        }).start();
    }
}

Output:

1次获取锁,这个锁是:com.xiangxue.可重入锁.NotReentrantLock@15c02541

You can see that the same thread is blocked when the lock is acquired for the second time

Reference:
https://blog.csdn.net/w8y56f/article/details/89554060
https://blog.csdn.net/qq_29519041/article/details/86583945

Guess you like

Origin blog.csdn.net/langwuzhe/article/details/108556899