从ReentrantLock的lock和unlock方法理解AQS运行过程

目录

1.ReentrantLock的原理

2.公平锁与非公平锁

3. 从非公平锁的lock和unlock方法理解AQS

A线程中lock.lock()方法

B线程中的lock.lock()方法

tryAcquire(arg)方法

addWaiter(Node.EXCLUSIVE)方法

acquireQueued()方法

C线程中lock.lock()方法

A线程中lock.unlock()

B线程(C线程同理)


0.来源说明

本文是对尚硅谷2020最新版Java面试题第三季(java大厂面试题,周阳主讲)中的AQS 视频资料的学习整理

1.ReentrantLock的原理

2.公平锁与非公平锁

3. 从非公平锁的lock和unlock方法理解AQS

public class AQSDemo {

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

        //通过一个银行办理业务的案例来模拟AQS是如何进行线程管理和通知唤醒机制
        //3个线程模拟三个来银行办理业务的顾客

        //A顾客,第一个顾客,此时银行窗口没有任何人,A可以直接办理业务
        //办理业务时间20分钟
        new Thread(()->{
            lock.lock();
            try {
                System.out.println("A thread come in.......");
                TimeUnit.MINUTES.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "A").start();

        //B顾客,第二个顾客,由于银行窗口只有一个, 此时B只能等待
        //进入到候客区
        new Thread(()->{
            lock.lock();
            try {
                System.out.println("B thread come in.......");
            } finally {
                lock.unlock();
            }
        }, "B").start();

        //C顾客,第三个顾客,由于银行窗口只有一个,此时C只能等待
        //进入候客区
        new Thread(()->{
            lock.lock();
            try {
                System.out.println("C thread come in.......");
            } finally {
                lock.unlock();
            }
        }, "C").start();
    }
}

AQS源码深度分析走起

  • lock()---->acquire()--->tryAcquire(arg)--->addWaiter(Node.EXCLUSIVE)--->acquireQueued(addWaiter(Node.EXCLUSIVE))
  • unlock()

A线程中lock.lock()方法

A顾客想要办理业务(对应调用A线程中lock.lock()),由于此时受理窗口没人,可直接办理业务。

运行完红框代码后,状态如下:

A顾客办理业务需要20分钟,将一直占着业务办理窗口。

B线程中的lock.lock()方法

B顾客想要办理业务(对应B线程中的lock.lock())

由于此时A顾客在占着窗口,B将进入acquire(1)分支。

tryAcquire(arg)方法

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
        final boolean nonfairTryAcquire(int acquires) {
            //current线程为B线程
            final Thread current = Thread.currentThread();
            //由于A线程占用着,因此state值为1
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //current线程为A线程,ownerThread为A线程,所以不相等
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            //因此,最终返回false
            return false;
        }

由于返回false,!tryAcquire(1)为true,继续进行if判断。

addWaiter(Node.EXCLUSIVE)方法。

    private Node addWaiter(Node mode) {
        //当前线程为B线程
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //由于初始时tail为null,因此将进入enq方法
        enq(node);
        return node;
    }
    //完成的工作就是把B线程的节点加入到队列中
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            //如果tail为null,说明队列中没有Node,需要初始化,生成哨兵节点
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                //第二次for循环将走到这里
                //将B线程的Node加入到队列中
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

此时的图示如下:

acquireQueued()方法

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            //这里会将哨兵节点的waitStatus设置为signal
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

C线程中lock.lock()方法

C线程的运行过程和B线程一样,最终B、C线程都将挂起阻塞在LockSupport.part(this)那个地方,不会再继续向下执行

A线程中lock.unlock()

    public void unlock() {
        sync.release(1);
    }

B线程(C线程同理)

程序继续向下执行,将进行下一次for循环,运行到如下红框代码,将B线程的Node从队列中移除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值