并发编程之--AQS(抽象同步队列)

本文深入解析了AQS(Abstract Queued Synchronizer)的工作原理,包括其作为FIFO双向链表的数据结构特性、如何通过state变量实现不同类型的锁机制、独占锁与共享锁的区别、以及公平锁与非公平锁的具体实现。
摘要由CSDN通过智能技术生成

AQS(抽象同步队列)

1.juc包中各种锁都是基于其实现的

AQS是一个FIFO的双向链表,其内部有一个int类型的参数state 可以通过 getState setState compareAndSetState 函数修改其值 对于 ReentrantLock 现来说 state 可以用 来表示前线程获取锁的可重入次数 ;对于 读写锁 ReentrantReadWriteLock 来说 state 16位表示读状态,也就是获取该读锁的次数,低 16 位表示获取到写锁的线程的可重入次数;对于 semaphore 来说,state 用来表示当前可用信号的个数;对于 CountDownlatch 来说,state 来表示计数器当前的值

2.AQS的独占锁和共享锁

对于AQS 来说,线程同步的关键是对状态值 state 进行操作 根据 state 是否属于一个线程 ,操作 state 方式分为独占方式和共享方式 在独占方式下获取和释放资源使用的
方法为 void acquire( int arg) void acquirelnterruptibly(int arg) boolean release( int arg)
在共享方式下获取和释放资源的方法为: void acquireShared(int arg) void
acqt eSharedinterruptibly(int arg) boolean easeShared(int arg)

3.独占锁(排它锁)的获取和释放锁:

独占锁:

获取锁 acquiref方法):锁与具体某个线程绑定,当某个线程获取到锁时其他线程再获取该锁时会被阻塞。
如reentrantLock,AQS内部会使用CAS先把state从0变为1,并把当前锁的持有者设置为该线程,当该线程再次获取锁时state由1变为2,即设置可重入次数。其他线程尝试获取锁时,state不为0且该锁持有者不是当前线程则被放入阻塞队列队尾后调用Locksupport.park(this)将该线程挂起。
释放锁 release方法):先CAS把state从减一变为0,当变为0时,将持有锁的线程设置为null。同时调用LockSupport.unpark(thread) 将阻塞队列里的线程唤醒。

共享锁:

ReentrantReadWriteLock 里的读锁是共享锁,写锁是独占锁。对于读锁获取和释放锁就是使用 CAS把state的高16位进行增减。

        protected final int tryAcquireShared(int unused) {
            /*
             * 
             */
             // (1)获取当前状态值
            Thread current = Thread.currentThread();
            int c = getState();
           //(2)判断是否写锁被占用
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
             //(3)获取读锁计数
            int r = sharedCount(c);
            // (4)尝试获取锁 多个读线程只有 1个会成功,不成功的进入fullTryAcquireShared进行重试
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                // (5 )第1个线程获取读锁
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                 //(6)如采当前线程是第1个获取读锁的线程
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                // (7)记录最后1个获取读锁的线程或记录其他线程读锁的可重入数
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
           // 类似tryAcquireShared ,但是自旋获取
            return fullTryAcquireShared(current);
        }

4.AQS的入队尾操作:

1.先初始化队列的哨兵节点,头尾都指向哨兵节点,然后将要插入的node节点cas设置为尾部节点,node节点前驱节点设置为原来的尾部节点,尾部节点的后驱节点设置为该node节点:
``

 private Node addWaiter(Node mode) {
        Node node = new Node(mode);  

        for (;;) {
            Node oldTail = tail;
            if (oldTail != null) {
            //设置前置节点为原来的尾节点
                node.setPrevRelaxed(oldTail);
                if (compareAndSetTail(oldTail, node)) {
                //设置原来的尾部节点的后置节点为该node节点
                    oldTail.next = node;
                    return node;
                }
       } else {
            //初始化队列哨兵节点,头和尾指向哨兵节点
                initializeSyncQueue();
            }
}

在这里插入图片描述

Reentrantlock的公平锁和非公平锁(默认)

下面是非公平锁源码:这里线程在获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程 而是使用了抢夺策略

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //这里线程在获取锁前并没有查看当前AQS队列里面是否有比自己更早请求该锁的线程 而是使用了抢夺策略
                if (compareAndSetState(0, acquires)) {
                    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;
        }

下面是公平锁源码

      protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
            //以下是公平锁的实现策略:如果当前队列为空或者当前线程的节点是队列的第一个节点
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    
    //公平锁的实现方法,这个方法表示是否有前驱节点
     public final boolean hasQueuedPredecessors() {
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
            /** 
            h!=t 表示当前队列不为空 ;
            如果 h!=t 并且 ==null 则说明有一个元素将要作为 QS 第一个节点入队列;
         如果 h!=t 并且 s!=null && s.thread != Thread.cunentThread()则说明队列 面的第1个元素不是当前线程,
          */
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值