并发编程JUC的AQS底层源码

本文通过ReentrantLock实例,揭示了AQS(AbstractQueuedSynchronizer)在Java并发中作为核心的角色,介绍了CLH队列的工作原理,以及非公平锁如何决定线程获取锁的顺序。重点讲解了锁的争夺、线程阻塞与唤醒的过程。
摘要由CSDN通过智能技术生成

AQS是JUC的核心,明白AQS基本上JUC下的工具类就会使用:

AQS其实就是CLH队列(很想双向链表+state状态)

故事背景:最近看七龙珠

以ReentrantLock为例

package JUC;


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

public class test {

    public static void main(String[] args) {
        
        
        
        ReentrantLock lock = new ReentrantLock();
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("沙鲁与孙悟空");
                //战斗的时间,
                try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            }finally {
                lock.unlock();
            }
        },"Thread孙悟空").start();




        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("沙鲁与贝吉塔");
                //战斗的时间,
                try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            }finally {
                lock.unlock();
            }
        },"Thread贝吉塔").start();



        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("沙鲁与孙悟饭");
                //战斗的时间,
                try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            }finally {
                lock.unlock();
            }
        },"Thread孙悟饭").start();

    }
}

简单说一下:ReentrantLock

  //下面  删掉 

简单说一下:AbstractQueuedSynchronizer

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    //AbstractOwnableSynchronizer是一个抽象类其实就一个地方用到,后面会有
    private static final long serialVersionUID = 7373984972572414691L;

    /**
     * Creates a new {@code AbstractQueuedSynchronizer} instance
     * with initial synchronization state of zero.
     */
    protected AbstractQueuedSynchronizer() { }

    /**
     * Wait queue node class.
     *

     * <pre>
     *      +------+  prev +-----+       +-----+
     * head |      | <---- |     | <---- |     |  tail
     *      +------+       +-----+       +-----+
     * </pre>



     */


    //这个Node类相当于链表中的节点


    //volatile Node prev; 前指针
    //volatile Node next; 后指针
    //volatile int waitStatus; 节点的状态
    //后面是删减版



    static final class Node {

        static final Node SHARED = new Node();

        static final Node EXCLUSIVE = null;


        volatile int waitStatus;


        volatile Node prev;


        volatile Node next;

        volatile Thread thread;


        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }




        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

    //这个不属于链表的,这个算是上下 链表属于左右,看图就能明白
    private transient volatile Node head;

    private transient volatile Node tail;
    //这个状态是   是否有线程占用lock锁,没线程占用就可以直接获得,不需要进入队列(双向链表)中等待
    private volatile int state;

a.步骤如下:
Thread孙悟空刚刚来到其他人还没有来,发现state = 0,可以直接占用

步骤1:lock.lock(),调用ReentrantLock类的lock()方法

步骤2:lock()方法内调用的Sync类的lock方法()  是一个抽象方法             //这个Sync继承了AbstractQueuedSynchronizer(AQS)

步骤3:lock是抽象方法,有两个实现方法 一个是公平锁FairSync(true)一个是非公平锁NonfairSync(默认是非公平锁,false 为非公平锁)

步骤4:非公平锁的实现,AQS 就是 state + CLH ,当代码第一次走到这里时,state = 0

compareAndSetState(0,1)  CAS操作,期待state = 0 ,如果为0 把state比较并交换为1  ,设置独占线程set Exclusive Owner Thread为Thread孙悟空

    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;  //独占线程 为 Thread 孙悟空
    }

b.步骤如下:

Thread贝吉塔为第二个来到的人,发现沙鲁正在和Thread孙悟空正在战斗(state = 1),也就是说Thread贝吉塔尝试获取lock(沙鲁)失败,进入阻塞队列中

前三步和Thread孙悟空一样,第四部CAS失败if(false) 走 else  {

acquire(1);    // 这个1 表示 重复次数,重入锁与这个数值有关

}里面,进入第五步

步骤5 acquire方法是AQS内方法,传参arg为1

步骤6 tryAcquire属于设计模式中模板设计模式是一个钩子方法,子类必须实现否则会抛出异常

步骤7 NonfairSync非公平锁实现,其实就是ReentrantLock(子类)去实现了tryAcquire方法

步骤8 return NonfairTryAcquire(1),方法在ReentrantLock有具体实现过程

步骤9 详细介绍如下

        final boolean nonfairTryAcquire(int acquires) {

            //获取当前线程  即Thread贝吉塔
            final Thread current = Thread.currentThread();
            //获取state的值,现在为1,因为被Thread孙悟空占用
            int c = getState();
            //判断state 是否为0  当然有可能为0只要在这一时刻Thread孙悟空释放lock锁,就可以重启抢到lock锁,就是是state的状态为0时,当然这样的概率 和中彩票一样的
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);  //在中彩票的情况下,把独占线程设置为Thread贝吉塔
                    return true;
                }
            }
            //这里判断自己是否是占用lock,如果是自己 state++(加一表示重入一次),也是ReentrantLock具有可重入性的原因
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires; //acquires为已经重入多少次
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                
                setState(nextc);
                return true;
            }
            //一般以false结束
            return false;
        }

步骤10:return false的返回结果,!tryAcquire(arg)为true,执行&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg)  当然先执 addWaiter(Node.EXCLUSIVE)

步骤11:执行addWaiter

    private Node addWaiter(Node mode) {
        //这个node为当前线程,第一次执行为node为Thread贝吉塔
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //第一次执行时,tail为null ,因为现在CLH还没有真正连接,所以CLH的tail指向为null
        Node pred = tail;
        if (pred != null) {   
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //第一次执行直接会跳到这里
        enq(node);
        return node;
    }

步骤12: enq入队操作

 这个就是很优雅的代码,是形成CLH队列的代码

    private Node enq(final Node node) {
        for (;;) {
            

            //第一次执行tail为null,所以t为null
          


            //::::第二次 t = head = tail
            Node t = tail;
            if (t == null) { // Must initialize
                //compareAndSetHead(new Node())
                //new Node()为哨兵节点 
                if (compareAndSetHead(new Node()))   // 这里就是设置一个new Node() 为Head,     
              
              
              //下面这三行代码是我截取的compareAndSetHead 下面的代码
              /*
              private final boolean compareAndSetHead(Node update) {
              return unsafe.compareAndSwapObject(this, headOffset, null, update);
              }
                   
              */
                    //第一次执行是tail的地址和head的地址完全相等
                    
                    tail = head;

            } else {
                //::::node.prev = head 就是node的前指针指向head (Thread贝吉塔 的prev 的前指针指向 head(head 为一个哨兵节点))
                node.prev = t;  //只有第一次head == tail (当只存在哨兵节点时),后面tail队列中最后一位Node节点
                //::::设置tail为node地址,这里和第一次不一样
                if (compareAndSetTail(t, node)) { /* cas 一定为比较并交换 这里我猜是发生了 
                                      
                 完全看不懂下面的代码,只有tail.next = node 这样才能形成 CLH 队列
                 private final boolean compareAndSetTail(Node expect, Node update) {
                 return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
                 }

                                     */
                    //::::t.next = tail  
                    t.next = node;  //tail 指向 node 节点
                    return t;
                }
            }
        }
    }

步骤13:

acquireQueued(final Node node, int arg) {}
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //取Thread贝吉塔的前置Node(这里是哨兵节点)
                final Node p = node.predecessor();
                //tryAcquire(arg)再次尝试获取lock,这里
                if (p == head && tryAcquire(arg)) {
                    setHead(node);  //这个在unpark下执行 他写的代码是真的优雅,这个如果线程贝吉塔获得 lock 那么让哨兵节点的next指向 null
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

步骤14:

       //获取node前一个节点,Thread贝吉塔的前一个节点为哨兵节点 
       final Node predecessor() throws NullPointerException {
            Node p = prev;
            //不存在会抛出异常
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

步骤15: 第一次,把ws的值变成了-1

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //::::表示第二次,看步骤13就知道在for循环内
        
        //第一次ws = 0;
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)  //Node.SIGNAL 为-1
            //::::第二次返回true
            /*
             * 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.
             */
            //第一次,把ws的值变成了-1
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

步骤16:  阻塞Thread线程

    private final boolean parkAndCheckInterrupt() {
        //Thread贝吉塔被park阻塞,休眠中(这个是关键点,后面怎样唤醒Thread贝吉塔线程才是关键)
        LockSupport.park(this);
        return Thread.interrupted();
    }

同理:Thread孙悟饭也会加入阻塞队列中

接下来就是lock.unlock()阶段,当Thread孙悟空释放lock(不在于沙鲁战斗时),Thread贝吉塔和Thread孙悟饭线程获得lock的呢

步骤1:调用sync.release(1)   

步骤2:   调用tryRelease(1)

步骤3:   调用

UnsupportedOperationException(); 这个是模板方法,必须在子类中实现

步骤4 :tryRelease

        protected final boolean tryRelease(int releases) {
            //getState就是与state的值,releases = 1 所以c等于0
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                //设置占用Lock为null state = 0,这样就可以有其他线程获得锁了(与沙鲁战斗的资格)
                setExclusiveOwnerThread(null);
            }
            //设置state为0
            setState(c);
            return free; //返回true
        }

步骤5返回true

    public final boolean release(int arg) {
        if (tryRelease(arg)) { //返回true
            //h为哨兵节点
            Node h = head;
            if (h != null && h.waitStatus != 0)
                //上面条件都成立
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

步骤6:

    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            //这里哨兵节点status变成了0
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        //s 为Thread贝吉塔
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //这个if成立,unpark(Thread贝吉塔)
        if (s != null)
            LockSupport.unpark(s.thread);
    }

回到lock的  步骤16:Thread贝吉塔成功被唤醒

步骤17:

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //lock步骤6、7、8、9 先看一下下面的解析
                if (p == head && tryAcquire(arg)) {
                    //设置head指向node
            /*
                    这里直接吧setHead方法复制过来了
                    
                    private void setHead(Node node) {
                    //设置head为node(说指向也可以,感觉都能理解,head指向node就是Thread贝吉塔(之前用用过的)地址变成了哨兵节点,当然真正的Thread贝吉塔已经抢到了lock(正在和沙鲁战斗))
                    head = node;
                    //彻底变为null 
                    node.thread = null;
                    node.prev = null;
                    }
     
            */
                   setHead(node);
                    //p为以前的哨兵节点.next说明被断开,这个哨兵已经不合任何节点相连
                    p.next = null; // help GC  GC垃圾回收  断开哨兵节点的next为null ,GC root 为空,在GC时会被回收
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

步骤17会用到lock步骤6、7、8、9,这里不粘贴图了

        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //设置state =1
                if (compareAndSetState(0, acquires)) {
                    //lock的占用线程为Thread贝吉塔
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

整个故事就是这样

为什么是非公平锁,似乎是公平的呀,因为后面来的线程有可能比先来的tryAcquire获得锁,我理解是这样,存在问题请指出

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值