Nucleic acid detection: let me understand the principle of AQS

table of Contents

Exclusive lock

Shared lock

synchronized lock does not respond to interrupt

AQS responds to interrupt

Conditional queue

to sum up


The Spring Festival is getting closer and the epidemic situation is getting more and more serious, but I can't stop the urge to reunite with Lian and his family in his hometown (Hubei). In response to national requirements, we went to do nucleic acid testing .

image.png

 

Exclusive lock


In the morning, Calling Lian brought a family of three to Nanjing No. 1 Hospital for nucleic acid testing. Miss nurses stood at the entrance of the hospital and told us that there are many people. Regardless of adults and children, we need to line up one by one to wait for the doctor to collect saliva for testing. OK, below Let's use the code + picture to see how our family of three lined up!

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-22 10:33
 * @description:独占锁测试
 * @modified By:
 * 公众号:叫练
 */
public class ExclusiveLockTest {

    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    //医院
    private static class Hospital {

        private String name;

        public Hospital(String name) {
            this.name = name;
        }

        //核酸检测排队测试
        public void checkUp() {
            try {
                writeLock.lock();
                System.out.println(Thread.currentThread().getName()+"正在做核酸检测");
                //核酸过程...难受...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Hospital hospital = new Hospital("南京市第一医院");
        Thread JLWife = new Thread(()->hospital.checkUp(),"叫练妻");
        JLWife.start();
        //睡眠100毫秒是让一家三口是有顺序的排队去检测
        Thread.sleep(100);
        Thread JLSon = new Thread(()->hospital.checkUp(),"叫练子");
        JLSon.start();
        Thread.sleep(100);
        Thread JL = new Thread(()->hospital.checkUp(),"叫练");
        JL.start();
    }
}

The above code: start three threads in the main thread to queue at the entrance of the hospital, the lady is the first , and the wife called Lian is the first one, the child called Lian is standing in the middle, and the last is called Lian You. We assume that it takes 3 seconds for each nucleic acid detection to be simulated. In the code, we used an exclusive lock. The exclusive lock can be understood as the hospital has only one doctor, and one doctor can only do nucleic acid for one person at the same time, so it needs to be tested one by one, so it takes 9 seconds to complete the code execution, and the nucleic acid detection can be all Done. The code logic is still relatively simple, the same as the synchronized description in our previous article. Let's describe the nucleic acid queue with a picture!

image.png

 

The full name of AQS is AbstractQueueSynchroniz, which means queue synchronizer, which is essentially a doubly linked list. In AQS, each thread is encapsulated into a Node node, and each node is added by tail interpolation. In addition, the node also encapsulates state information, such as whether it is exclusive or shared. For example, the above case means exclusive Node. The doctor itself is a shared resource. It is called state in AQS. It is represented by int type. Compete for state through CAS. If the thread grabs the lock, it increments. The thread that does not grab the lock will block and wait for the opportunity to be awakened. As shown in the figure below: The internal structure of AQS is abstracted according to our understanding.

image.png

According to the above description, you can see that AQS is to encapsulate threads with Node, and then connect the threads according to a doubly linked list of first come, last come ( except for unfair locks )! Regarding unfair locking, I also described it through simple examples in the case of "queuing for dinner". If you are interested in children's shoes, you can look through it!

 

 

Shared lock


The above process of making nucleic acid is executed synchronously, which is called exclusive lock. What does shared lock mean? Now the child named Lianzi is only 3 years old and cannot complete the nucleic acid test independently. The nurse sister feels the same. Observing that Lianzi is ranked behind the wife of Lianzi, she asks them to do the nucleic acid test at the same time. This operation of doing nucleic acid at the same time is equivalent to obtaining doctor resources at the same time, which we call a shared lock. Below is our test code.

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-21 19:54
 * @description:共享锁测试
 * @modified By:
 * 公众号:叫练
 */
public class SharedLockTest {

    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();

    //医院
    private static class Hospital {

        private String name;

        public Hospital(String name) {
            this.name = name;
        }

        //核酸检测排队测试
        public void checkUp() {
            try {
                readLock.lock();
                System.out.println(Thread.currentThread().getName()+"正在做核酸检测");
                //核酸过程...难受...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                readLock.unlock();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Hospital hospital = new Hospital("南京市第一医院");
        Thread JLWife = new Thread(()->hospital.checkUp(),"叫练妻");
        JLWife.start();
        //睡眠100毫秒是让一家三口是有顺序的排队去检测
        Thread.sleep(100);
        Thread JLSon = new Thread(()->hospital.checkUp(),"叫练子");
        JLSon.start();
        /*Thread.sleep(100);
        Thread JL = new Thread(()->hospital.checkUp(),"叫练");
        JL.start();*/
    }
    
}

In the above code, we use ReentrantReadWriteLock.ReadLock as the read lock, and start the two threads of "Calling Wife" and "Calling Training" on the main thread. Originally, it takes 6 seconds for the mother and child to complete, but now it only takes 3 seconds to complete , The benefit of shared locks is higher efficiency. As shown in the figure below, it is the state of the Node node in AQS at a certain time. Compared with the above figure, the state of Node has become a shared state, and these nodes can share doctor resources at the same time !

image.png

 

synchronized lock does not respond to interrupt


/**
 * @author :jiaolian
 * @date :Created in 2020-12-31 18:17
 * @description:sync不响应中断
 * @modified By:
 * 公众号:叫练
 */
public class SynchronizedInterrputedTest {

    private static class MyService {

        public synchronized void lockInterrupt() {
            try {
                System.out.println(Thread.currentThread().getName()+" 获取到了锁");
                while (true) {
                   //System.out.println();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService myService = new MyService();
        //先启动线程A,让线程A先拥有锁
        Thread threadA = new Thread(()->{
            myService.lockInterrupt();
        });
        threadA.start();
        Thread.sleep(1000);
        //启动线程B,中断,synchronized不响应中断!
        Thread threadB = new Thread(()->{
            myService.lockInterrupt();
        });
        threadB.start();
        Thread.sleep(1000);
        threadB.interrupt();
    }
}

Such as the above code: first start thread A, let thread A hold the lock first, sleep for 1 second and then start thread B to make thread B in a runnable state, and then interrupt thread B after 1 second. The output on the console is as follows: The A thread acquires the lock, and the console does not immediately output an error message after waiting for 2 seconds. The program has not finished execution, indicating that the synchronized lock does not respond to the interrupt. The thread interruption report is output only after the B thread acquires the lock. information!

image.png

 

AQS responds to interrupt


Only by frequently doing comparison knowledge can we get through. Lock provides lock and lockInterruptibly two ways to acquire a lock. The lock method and synchronized do not respond to interrupts. Then let's see what lockInterruptibly means to respond to interrupts. We still use the nucleic acid case to illustrate.

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-22 15:18
 * @description:AQS响应中断代码测试
 * @modified By:
 * 公众号:叫练
 */
public class AQSInterrputedTest {

    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    //医院
    private static class Hospital {

        private String name;

        public Hospital(String name) {
            this.name = name;
        }

        //核酸检测排队测试
        public void checkUp() {
            try {
                writeLock.lockInterruptibly();
                System.out.println(Thread.currentThread().getName()+"正在做核酸检测");
                //核酸过程...难受...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Hospital hospital = new Hospital("南京市第一医院");
        Thread JLWife = new Thread(()->hospital.checkUp(),"叫练妻");
        JLWife.start();
        //睡眠100毫秒是让一家三口是有顺序的排队去检测
        Thread.sleep(100);
        Thread JLSon = new Thread(()->hospital.checkUp(),"叫练子");
        JLSon.start();
        Thread.sleep(100);
        Thread JL = new Thread(()->hospital.checkUp(),"叫练");
        JL.start();
        //等待1秒,中断叫练线程
        System.out.println("护士小姐姐想和叫练私聊会!");
        Thread.sleep(1000);
        JL.interrupt();
    }
}

The above code: Called Lian's family of three use exclusive locks to queue up to do nucleic acid, call Lian thread to wait for one second, Miss Nurse wants to have a private chat with Call Lian! Could it be that Miss Sister would have any thoughts, so called Lian immediately interrupted this nucleic acid test, and noticed that it was interrupted immediately . The console print results are as follows: Both the Lianqi thread and the Lianzi thread have done nucleic acid, but the Lianzi thread did not succeed! Because it was interrupted by the nurse sister, the result is as shown in the figure below. So we can conclude that the lock in aqs can respond to interrupts. Now, what happens if the lockInterruptibly method in the above code is replaced with the lock method. If it is changed to this method, Miss Sister will come and tease me again. If you call Lian you must successfully obtain the lock first, that is to say, Lian has come to the doctor and is ready to do it. Nucleic acid is gone, Miss Sister suddenly said that she had something to call for training, which eventually led to Calling Lian not doing nucleic acid . When encountering such a thing, she could only say that Miss Sister was deliberate, and Miss Sister was too bad . Regarding the test that the lock method does not respond to interruption, you can test it yourself. See if I wronged the nurse sister.

We can conclude that in aqs, if a thread is acquiring a lock or is in a waiting state, and another thread interrupts the thread, responding to interruption means that the thread is interrupted immediately, and not responding to interruption means that the thread needs to acquire the lock Then interrupt again.

image.png

image.png

 

Conditional queue


Life may be so unsatisfactory. The long hour of waiting in line finally passed. It was our turn to prepare for nucleic acid. You said you were out of breath , and you brought your ID card every time you asked Lian's wife to go out, but you forgot to go home this time? Let's use the code to see what happened in the process of making nucleic acid for a family of three called Lian? How did you deal with it again!

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author :jiaolian
 * @date :Created in 2021-01-22 16:10
 * @description:条件队列测试
 * @modified By:
 * 公众号:叫练
 */
public class ConditionTest {

    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    //条件队列
    private static Condition condition = writeLock.newCondition();

    //医院
    private static class Hospital {

        private String name;

        public Hospital(String name) {
            this.name = name;
        }

        //核酸检测排队测试
        public void checkUp(boolean isIdCard) {
            try {
                writeLock.lock();
                validateIdCard(isIdCard);
                System.out.println(Thread.currentThread().getName()+"正在做核酸检测");
                //核酸过程...难受...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
                System.out.println(Thread.currentThread().getName()+"核酸检测完成");
            }
        }

        //校验身份信息;
        private void validateIdCard(boolean isIdCard) {
            //如果没有身份信息,需要等待
            if (!isIdCard) {
                try {
                    System.out.println(Thread.currentThread().getName()+"忘记带身份证了");
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        //通知所有等待的人
        public void singleAll() {
            try {
                writeLock.lock();
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                writeLock.unlock();
            }
        }

    }


    public static void main(String[] args) throws InterruptedException {
        Hospital hospital = new Hospital("南京市第一医院");
        Thread.currentThread().setName("护士小姐姐线程");
        Thread JLWife = new Thread(()->{
            hospital.checkUp(false);
            },"叫练妻");
        JLWife.start();
        //睡眠100毫秒是让一家三口是有顺序的排队去检测
        Thread.sleep(100);
        Thread JLSon = new Thread(()->hospital.checkUp(true),"叫练子");
        JLSon.start();
        Thread.sleep(100);
        Thread JL = new Thread(()->{
            hospital.checkUp(true);
        },"叫练");
        JL.start();
        //等待叫练线程执行完毕
        JL.join();
        hospital.singleAll();
    }

}

The code above: A family needs to queue up for testing to obtain an exclusive lock. Ask Lian's wife to go first to prepare for nucleic acid. The nurse said that you must swipe your ID before you can go in. The asked Lian's wife suddenly recalled that she went out in a hurry and forgot to bring her ID card. What can I do, do I need to re-queue? Calling Lian's wife is very panic. The nurse said, otherwise, you should hurry home to get it, and wait for Lianzi. After calling Lianzi, I will quickly arrange for you to go in for nucleic acid treatment, so that you don’t need to do the nucleic acid. Queued, this is the meaning of the above code. Let's take a look at the execution results as shown in the figure below, which are consistent with the results of our analysis. The place where the red circle is drawn at the end of the figure below is called Lian's wife to complete the nucleic acid test. Let's take a look at the internal process of AQS.

image.png

 

As shown in the figure below, when the wife is called to obtain the lock first, it is found that the ID card forgets to call the await method to release the lock held, and puts itself as the node node at the end of the condition queue. At this time, the condition queue is empty, so the condition queue There is only one thread called Lian's wife, and then the nurse sister will release the resource of Nucleic Acid Doctor and allocate it to the next waiter, which is called Lianzi thread. Similarly, after calling Lianzi finish executing and releasing the lock, the called Lianzi thread will be awakened. , The bottom layer is to use LockSupport.unpark to complete the wake-up operation, which is equivalent to the wait/notify/notifyAll methods in the basic series. When the call-training thread is executed and there are no threads behind, the nurse lady calls the singleAll method to meet the call-training wife thread in the condition queue to wake up, and joins the end of the AQS to wait for execution. The condition queue is a singly linked list, and one AQS can correspond to multiple condition queues through newCondition(). Here we will not use the code alone for testing.

image.png

 

to sum up


Today we explained several important concepts of AQS with code + pictures + stories. We have sorted them out and hoped to be helpful to you. The writing is not complete. At the same time, there are many places that need to be corrected. I hope you can correct and comment. This period of time ago will continue to output the realization of AQS advanced locks, such as: ReentrantLock, thread pool concepts and so on. Finally, please like and pay attention to the ones you like. My name is Lian [ Official Account ] , and I call and practice while calling .

 

Note: This story is my own fictitious, it is only for your reference and understanding. I hope everyone can go home and reunite smoothly during the New Year!

tempImage1611306633088.gif

Guess you like

Origin blog.csdn.net/duyabc/article/details/113062010