JAVA并发编程-AQS原理&RenntrantLock源码导读

 1.文章目录

  • AQS概述,类结构,源码导读;
  • RenntractLock源码,实现细节导读;

2.AQS概述&类结构

  AbstractQueuedSynchronizer 抽象同步队列简称 AQS ,它是实现同步器的基础组件, 并发包中锁的底层就是使用 AQS 实现的;

  • AQS 是一个FIFO 的双向队列,其内部通过节点 head tail 首和队尾元素,队列元素的类型为Node 其中 Node 中的 thread 变量用来存放进入 AQS 队列里面的线程:
  • Node 节点内部的 SHARED 用来标记该线程是获取共享资源资源时被阻 挂起后放入AQS 队列的, EXCLUSIVE 用来标记线程是获取独占资源时被挂起后放入 AQS 队列的,waitStatu 记录当前线程等待状态,可以为 CANCELLED (线程被取消了)、 SIGNAL 线程需要被唤醒)、 ONDITION (线程在条件队列里面等待〉、 PROPAGATE 释放共享资源时需要通知其他节点; prev 记录当前节点的前驱节点, next 记录当前节点的 后继节点
  • AQS 维持了的状态信息 state,可以通过 getState setState compareAndSetState 函数修改其值,对于 ReentrantLock来说, state 可以用来表示前线程获取锁的可重入次数 ;对于 读写锁 ReentrantReadWri teLock 来说 state16 位表示读状态,也就是获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数; 对于 semaphore 来说 state 用来表示当前可用信号的 数:对于 CountDownlatch 来说,state C表示 数器当前的值;
  • AQS 有个内部类ConditionObject 用来结合锁实现线程同步 ConditionObject 可以 直接访问 AQS 内部的变量,比如 state 态值和 AQS队列。ConditionObject 是条 变量 个条件变量对应 件队列 (单向链表队列),其用来存放调用条件变 await 方法后被阻塞的线程,如类图所示 这个条件队列的头、尾元素分别为firstWaiter,lastWaiter;

源码导读

基本属性:看上面的图可以直到基本属性:使用Unsafe机制,记录变量偏移量,方便CAS操作


    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
     }

acquire(int arg):获取独占资源方法

 public final void acquire(int arg) {
        // 首先调用tryAcquire尝试获取资源
        if (!tryAcquire(arg) &&
            ,失败则(Node.EXCLUSIVE)状态进去等待队列,并且LockSupport.park挂起自己
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

addWaiter方法

 private Node addWaiter(Node mode) {
        // 创建节点,记录当前线程
        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;
            CAS插入
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果上面没有插入成功,则循环插入
        enq(node);
        return node;
    }
  private Node enq(final Node node) {
        // 循环插入
        for (;;) {
            Node t = tail;
            // 尾部为NULL,则head肯定也是NULl,创建比赋值
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                // 设置tail节点
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

realease(int arg):释放资源

  public final boolean release(int arg) {
        // 调用tryRelease方法,主要是修改state的值
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                // 释放AQS队列一个被阻塞的线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

acquireShared(int arg)获取共享资源

// 首先尝试获取资源,如果不成功,LockSupport.park当前线程,封装Node.ShARED进入AQS队列
public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

doRelaseShared:释放共享资源

     // 尝试释放资源(设置state),如果成功则激活一个线程,判断是否满足自己的需要,不满足再次进去队列
     public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
  • 到此基本的核心方法已经导读结束,AQS作为队列同步的抽象,对基本方法做了实现,tryAcquire,treRelease等方法要交给子类来实现,比如可重入锁,不可重入锁的设计肯定不同的;后续对ReentrantLock源码导读会更加清晰;
  
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

   
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

    
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }

条件变量Condition

     这个变量类似于wait,notify的作用,线程同步用的,比较基础;可以自己探索下;

3.ReentrantLock类结构&源码导读

  • 可以看出,ReentrantLock实现了Lock接口,实现了基本接口,借助Sync(继承AQS)实现真正的线程同步策略;
  • ReentrantLock 是可重入的独占锁只能有一个线程可获取该锁,其他获取该锁的线程会被阻塞而被放入该锁的AQS阻塞队列里面;
  • 我们平常用这个的锁,有公平锁,非公平锁(默认)两种形式;因此内部封装了两种实现形式;

初始化方法:

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  • 上述代码提供了两种初始化方式,也是我们最常用的;默认为非公平策略,也可以根据fair参数指定;

其他核心方法:

   public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

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

   
    public Condition newCondition() {
        return sync.newCondition();
    }

    public int getHoldCount() {
        return sync.getHoldCount();
    }

   
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    
    public boolean isLocked() {
        return sync.isLocked();
    }

   // sync是否是FairSync的子类/类
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

   
    protected Thread getOwner() {
        return sync.getOwner();
    }

  
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

 
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
  • 可以看出都是借助Sync(AQS)变量实现的;模板方法设计;
  • 因此我们看Sync的实现

Sync类实现

  •  该类继承AQS,对AQS的方法进行了重写,根据Reentrant策略进行设计;
  • 具体的lock,tryRe
 abstract static class Sync extends AbstractQueuedSynchronizer {
.........
   // 交给fairSync,NoFairSync实现
   abstract void lock();
}

final boolean nonfairTryAcquire(int acquires)

// 非公平策略尝试获取资源
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果c=0则表示没有锁占有
            if (c == 0) {
                // CAS设置state=acquires
                if (compareAndSetState(0, acquires)) {
                     // 设置锁拥有线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 也是可重入锁的设计特点
            // 锁被占有了,看下是不是自己
            else if (current == getExclusiveOwnerThread()) {
                // 是自己,对c进行加
                int nextc = c + acquires;
                // 超过最大数
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

tryRelease:释放资源

protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            // 如果不是锁拥有线程,抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
           // 如果c=0了,释放资源完了
            if (c == 0) {
                // free=true,锁拥有线程为Null
                free = true;
                setExclusiveOwnerThread(null);
            }
             // CAS设置锁状态
            setState(c);
            return free;
        }

isHeldExclusively

//判断锁是否被自己占有,判断食不知自己就好了
 protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
}

几个简单方法:

     // 生产条件变量  
    final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // 获取锁Owner

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
        // 获取锁持有数
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
        // 是否被占有
        final boolean isLocked() {
            return getState() != 0;
        }
  • 到这几个基本方法介绍完了,我们看如何实现公平策略,非公平策略

FairSync&NoFairSync源码

NoFairSync类源码

  • 继承Sync对lock,tryAcquire做了重写;
  • 不公平体现在哪:第一个调用lock方法的线程就会获得,随机的
static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        // CAS设置state状态
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
               // 失败,调用AQS的acquire,前面已经介绍,如果尝试获取失败后,进入阻塞队列
                acquire(1);
        }
         // 服用父类Sync的方法
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

FairSync类源码

  • 继承Sync对lock,tryAcquire做了重写;
  • 公平体现在:根据阻塞队列从前向后以此获得锁,根据调用lock的顺序进行获得锁;
  • hasQueuedPredecessors实现了公平策略;
static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
         // 调用AQS上层方法,还是会掉tryAcquire方法
        final void lock() {
            acquire(1);
        }

        // 公平策略的实现
        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;
                }
            }
            //加state
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

    // 如果当前线程节点有前驱节点则返回住时, 如果当前AQS队列空或者当前线程节点是 AQS 第一个节 
   点则返回 false 。其中如果 h==t 说明当前队列,直接返回 false ;
   public final boolean hasQueuedPredecessors() {
        Node t = tail; 
        Node h = head;
        Node s;

        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

4.总结

  • 以上是AQS,ReentrantLock的基本实现原理,对于更多细节可以自己阅读;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章