Enter AQS from ReentrantLock

AQS source code reading

Today, from the lock method of ReentrantLock, we will go deep into AQS a little bit to see how AQS achieves the purpose of locking and unlocking. First, test what happens when only one thread locks, and then test how to deal with multiple threads. , and finally look at how the unlock operation wakes up other threads.

only one thread

test code

public class TestReentantLock {
    static ReentrantLock lock = new ReentrantLock(true);
    static Runnable r1 = ()-> {
        // 断点位置
        lock.lock();
    };
    public static void main(String[] args) {
        new Thread(r1).start();
    }
}
复制代码

Implementation process

image-20220411194329982 image-20220411194455095 image-20220411194554405 image-20220411194743774 image-20220411195410278 image-20220411195543686 image-20220411195908861 image-20220411200033141

Summarize

Through this process you can get:

  1. When there is only one thread, the state value is set through CAS, indicating that the current lock is held by a thread
  2. When there is only one thread, the operations of nodes and queues are not involved

when two threads

test code

package process;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TestReentantLock {
    static ReentrantLock lock = new ReentrantLock(true);
    static Runnable r1 = ()-> {
        lock.lock();
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    };
    static Runnable r2 = ()->{
        // 断点位置
        lock.lock();
    };

    public static void main(String[] args) throws InterruptedException {
        new Thread(r1).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(r2).start();
    }
}
复制代码

Implementation process

image-20220411200636003 image-20220411200757320 image-20220411201532550 image-20220411201712606 image-20220411201851645 image-20220411202300875 image-20220411202515374 image-20220411202838888 image-20220411203147087 image-20220411203236845

Summarize

  1. When there are two threads, use LockSupport.park() to suspend the thread to achieve the purpose of not acquiring the lock
  2. By setting the waitStatus value of the previous node to Node.Signal to -1, it means to wake up the subsequent node, that is, itself.

unlock() operation

test code

package process;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TestReentantLock {
    static ReentrantLock lock = new ReentrantLock(true);
    static Runnable r1 = ()-> {
        lock.lock();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 断点处
        lock.unlock();
    };
    static Runnable r2 = ()->{
        lock.lock();
    };

    public static void main(String[] args) throws InterruptedException {
        new Thread(r1).start();
        TimeUnit.MILLISECONDS.sleep(500);
        new Thread(r2).start();
    }
}

复制代码

Implementation process

image-20220411204404870 image-20220411204504407 image-20220411204807717 image-20220411204944492 image-20220411205139959

Summarize

  1. Get the head node, and use the waitStatus of the head node to determine the unlocking of subsequent threads

Summarize

The way AQS achieves thread synchronization can be summarized as:

  1. Block the thread through LockSupport.park() to achieve the purpose of not being able to acquire the lock
  2. Atomically update the head and tail of the queue through CAS
  3. Through a volatile state, the purpose of identifying the state of the lock is achieved

Guess you like

Origin juejin.im/post/7085334357774172196