JAVA的AQS,CAS和Reentrantlock简单讲解

AQS

什么是AQS
aqs全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLock、CountDownLatch等。
AQS是一个抽象类,主要是通过继承的方式来使用,它本身没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。可以这么说,只要搞懂了AQS,那么J.U.C中绝大部分的api都能轻松掌握。

AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。是用来构建锁或者其它同步器组件的重量级基础框架及整个JUC体系的基石,通过内置的CLH(FIFO)队列的变种来完成资源获取线程的排队工作,将每条将要去抢占资源的线程封装成一个Node节点来实现锁的分配,有一个int类变量表示持有锁的状态,通过CAS完成对status值的修改(0表示没有,1表示阻塞)

AQS的核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。

详解AQS内部代码在这里插入图片描述分析源码
每个Node节点保存了当前线程的同步状态,等待状态,前驱和后继节点等。
Node是构成同步队列的基础,同步队列中首节点是获取到锁的节点,它在释放的时候会唤醒后继节点,后继节点获取到锁的时候,会把自己设为首节点,注意,设置首节点不需要使用CAS,因为在并发环境中只有一个线程都获取到锁,只有获取到锁的线程才能设置首节点。

static final class Node {
        // 共享模式下等待的标记
        static final Node SHARED = new Node();
        // 独占模式下等待的标记
        static final Node EXCLUSIVE = null;
 
        // 线程的等待状态 表示线程已经被取消
        static final int CANCELLED =  1;
        // 线程的等待状态 表示后继线程需要被唤醒
        static final int SIGNAL    = -1;
        // 线程的等待状态 表示线程在Condtion上
        static final int CONDITION = -2;
        
        // 表示下一个acquireShared需要无条件的传播
        static final int PROPAGATE = -3;
 
        /**
         *   SIGNAL:     当前节点的后继节点处于等待状态时,如果当前节点的同步状态被释放或者取消,
         *               必须唤起它的后继节点
         *         
         *   CANCELLED:  一个节点由于超时或者中断需要在CLH队列中取消等待状态,被取消的节点不会再次等待
         *               
         *   CONDITION:  当前节点在等待队列中,只有当节点的状态设为0的时候该节点才会被转移到同步队列
         *               
         *   PROPAGATE:  下一次的共享模式同步状态的获取将会无条件的传播
 
         * waitStatus的初始值时0,使用CAS来修改节点的状态
         */
        volatile int waitStatus;
 
        /**
         * 当前节点的前驱节点,当前线程依赖它来检查waitStatus,在入队的时候才被分配,
         * 并且只在出队的时候才被取消(为了GC),头节点永远不会被取消,一个节点成为头节点
         * 仅仅是成功获取到锁的结果,一个被取消的线程永远也不会获取到锁,线程只取消自身,
         * 而不涉及其他节点
         */
        volatile Node prev;
 
        /**
         * 当前节点的后继节点,当前线程释放的才被唤起,在入队时分配,在绕过被取消的前驱节点
         * 时调整,在出队列的时候取消(为了GC)
         * 如果一个节点的next为空,我们可以从尾部扫描它的prev,双重检查
         * 被取消节点的next设置为指向节点本身而不是null,为了isOnSyncQueue更容易操作
         */
        volatile Node next;
 
        /**
         * 当前节点的线程,初始化后使用,在使用后失效 
         */
        volatile Thread thread;
 
        /**
         * 链接到下一个节点的等待条件,或特殊的值SHARED,因为条件队列只有在独占模式时才能被访问,
         * 所以我们只需要一个简单的连接队列在等待的时候保存节点,然后把它们转移到队列中重新获取
         * 因为条件只能是独占性的,我们通过使用特殊的值来表示共享模式
         */
        Node nextWaiter;
 
        /**
         * 如果节点处于共享模式下等待直接返回true
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
 
        /**
         * 返回当前节点的前驱节点,如果为空,直接抛出空指针异常
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }
 
        Node() {    // 用来建立初始化的head 或 SHARED的标记
        }
 
        Node(Thread thread, Node mode) {     // 指定线程和模式的构造方法
            this.nextWaiter = mode;
            this.thread = thread;
        }
 
        Node(Thread thread, int waitStatus) { // 指定线程和节点状态的构造方法
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

AQS具有头节点和尾节点

/**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

独占式
通过调用acquire的方法获取同步状态,该方法忽略中断,线程获取同步状态失败后,进入同步队列,在对其进行中断操作后,线程不会从同步队列移除。首先调用tryAcquire方法获取同步状态,AQS并没有实现这个方法,具体的实现由它的继承类进行重写,比如ReentrantLock的Sync类。如果获取同步状态成功的直接返回true;如果获取同步状态失败的话,调用addWaiter方法把线程封装成一个Node节点添加到同步队列的尾部,最后调用acquireQueued方法使节点以自旋的方式获取同步状态,如果获取同步状态失败,要挂起线程,最后,线程如果在获取同步状态中和同步队列中被中断过,要进行自我中断。

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

addWaiter方法

    /**
     * 把Node节点添加到同步队列的尾部
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);  // 以独占模式把当前线程封装成一个Node节点
        // 尝试快速入队
        Node pred = tail;  // 当前队列的尾节点赋给pred
        if (pred != null) {  // 先觉条件 尾节点不为空
            node.prev = pred;  // 把pred作为node的前继节点
            if (compareAndSetTail(pred, node)) { //利用CAS把node作为尾节点
                pred.next = node;    // 把node作为pred的后继节点
                return node;       // 直接返回node
            }
        }
        enq(node);  // 尾节点为空或者利用CAS把node设为尾节点失败
        return node;
    }
    /**
     * 采用自旋的方式把node插入到队列中
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // 如果t为空,说明队列为空,必须初始化
                if (compareAndSetHead(new Node())) // 新建一个节点利用CAS设为头节点,就是这样的形式 head=tail=null
                    tail = head;
            } else {    // 尾节点不为空的情况
                node.prev = t;  // 把t设为node的前驱节点
                if (compareAndSetTail(t, node)) {  // 利用CAS把node节点设为尾节点
                    t.next = node;   // 更改指针  把node作为t的后继节点
                    return t;   // 直接返回t
                }
            }
        }
    }

acquireQueued方法

    /* 
     * 此主要是通过自旋方式获取同步状态
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;  // 默认线程没有被中断过
            for (;;) {
                final Node p = node.predecessor();  // 获取该节点的前驱节点p
                if (p == head && tryAcquire(arg)) {  // 如果p是头节点并且能获取到同步状态
                    setHead(node);                   // 把当前节点设为头节点
                    p.next = null;                  // 把p的next设为null,便于GC
                    failed = false;                 // 标志--表示成功获取同步状态,默认是true,表示失败
                    return interrupted;             // 返回该线程在获取到同步状态的过程中有没有被中断过
                }
                if (shouldParkAfterFailedAcquire(p, node) &&   // 用于判断是否挂起当前线程
                    parkAndCheckInterrupt())
                    interrupted = true;      
            }
        } finally {
            if (failed)   // 如果fail为true,直接移除当前节点
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire方法
这段代码用来检测是否挂起当先线程,分三种情况,第一种情况是前驱节点的 ws = singal,表示前驱节点释放同步状态的时候会唤醒当前节点,可以安全挂起当前线程;第二种情况是前驱节点被取消,那就从前驱节点继续往前遍历,直到往前找到第一个ws <= 0 的节点;第三种是前驱节点的 ws = 0,表示前驱节点获取到同步状态,当前线程不能挂起,应该尝试去获取同步状态,前驱节点的同步状态的释放正好可以让当前节点进行获取,所以使用CAS把前驱节点的ws设为singal,另外如果 ws =PROPAGATE,说明正以共享模式进行传播,也需要使用CAS把ws设为singal.

    /**
     * 如果线程获取同步状态失败就要检查它的节点status,要保证prev = node.prev
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;  // 获取当前节点的前驱节点的waitStatus
        if (ws == Node.SIGNAL)     
            /*
             *  如果前驱节点的ws = singal,表示前驱节点释放后会唤起当前线程,    
             *  可以安全的挂起当前线程
             */
            return true;   // 能够挂起当前线程直接返回true
        if (ws > 0) {
            /*
             * 前驱节点的ws > 0,说明ws = Cancelled,表示前驱线程被取消,
             * 从前驱节点继续往前遍历,直到找到第一个前驱节点的ws <= 0 为止
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * 这种情况表示前驱节点的 ws = 0 或者 ws = PROPAGATE,我们需要一个singal,但是
             * 不能挂起当前线程 
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

parkAndCheckInterrupted方法
调用LockSupport的park方法挂起当前线程,返回该线程是否被中断过,如果被中断过,直接设置interrupted = true.

    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
	如果获取同步状态失败,采用cancelAcquire方法取消当前节点

分三种情况进行考虑: node本身就是尾节点,直接把node的prev设为尾节点;node的prev不是头结点,直接把prev和node的next进行连接;node的prev是头结点,使用unparkSuccessor唤醒后继节点

    /**
     * 取消当前节点
     */
    private void cancelAcquire(Node node) {
        // 当前节点不存在的话直接忽略 
        if (node == null)
            return;
 
        node.thread = null;  // 把当前节点的线程设为null
 
        // 获取当前节点的前驱pred
        Node pred = node.prev;
        while (pred.waitStatus > 0)   // 如果prde的ws > 0,直接跳过pred继续往前遍历,直到pred的
            node.prev = pred = pred.prev;  // ws <= 0
 
        // 获取pred的后继predNext
        Node predNext = pred.next;
 
        // 把node节点的ws设为CANCELLED
        node.waitStatus = Node.CANCELLED;
 
        // 如果node是尾节点,利用CAS把pred设为尾节点,predNext为null
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // pred不是头结点 && pred的线程不为空 && pred.ws = singal
            // 利用CAS把node的next设为pred的next节点
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {   // node是头结点,唤起它的后继节点
                unparkSuccessor(node);
            }
 
            node.next = node; // node指向自己,便于GC
        }
    }

unparkSuccessor方法

    /**
     * 如果node存在唤醒它的后继节点
     */
    private void unparkSuccessor(Node node) {
        /*
         * 获取node的ws,如果ws<0,使用CAS把node的ws设为0,表示释放同步状态
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
 
        /*
         * 获取node的后继节点s,根据条件s = null 或者 s.ws > 0,从同步队列的尾部开始遍历,
         * 直到找到距node最近的满足ws <= 0的节点t,把t赋给s,唤醒s节点的线程
         * 如果s不为null && s的ws <= 0,直接唤醒s的线程
         */
        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 (s != null)
            LockSupport.unpark(s.thread);
    }
    /**
     * 当前线程的自我中断
     */
    private static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
	同步状态的释放,这个方法主要调用了tryRelease方法,这个方法在AQS中直接抛出异常,必须由继承它的子类去重写此方法,如果此方法返回成功释放同步状态,如果当前节点不是空并且ws!=0,直接调用unparkSuccessor方法唤醒当前节点的后继节点.
     /**
     * 以独占模式释放同步状态,当前线程释放同步状态的时候,会唤醒同步队列上的后继节点
     * 释放成功后之后直接返回true
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

共享式
独占式和共享式的最大不同就是在同一时刻能否有多个线程获取同步状态,通过调用acquireShared方法获取同步状态.
tryAcquireShared是留给子类去重写的,如果tryAcquireShared方法返回值<0,说明获取同步状态失败,执行doAcquireShared方法,在doAcquireShared再次调用tryAcquireShared方法,判断其返回值,若返回值<0,获取同步状态失败,需要进入同步队列进行等待,若返回值 >= 0,如果返回值=0,说明当前线程获取同步状态成功,其他线程无法获取,也就不需要唤醒它的后继节点进行传播.如果返回值>0,此时当前线程获取同步状态后要唤醒它的后继节点,让其他线程也尝试去获取同步状态.

   public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    /**
     * 以共享非中断获取同步状态
     */
    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

独占式获取同步状态之后,直接返回中断状态,结束流程,共享式则调用setHeadAndPropagate方法传播唤醒的动作.

private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // 保存当前的头节点
        setHead(node); // 把当前节点设为头节点
        /*
         * 这里有三种情况执行唤醒操作:1.propagate > 0,代表后继节点需要被唤醒
         *                          2. h节点的ws < 0或者 h=null
         *                          3. 新的头结点为空 或者 新的头结点的ws < 0
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;    // 找到当前节点的后继节点s
            if (s == null || s.isShared())   // s=null 或者 s是共享模式,调用doReleaseShared方法唤醒后继线程
                doReleaseShared();
        }
    }

doReleaseShared方法

    private void doReleaseShared() {
        /*
         * 注意,这里的头结点已经是上面新设定的头结点了,从这里可以看出,如果propagate=0,
         * 不会进入doReleaseShared方法里面,那就有共享式变成了独占式.
         */
        for (;;) {  // 这里一个死循环直到满足条件h=head才能跳出
            Node h = head;
            if (h != null && h != tail) {  // 前提条件-当前的头结点不为null && h不是尾节点
                int ws = h.waitStatus;   
                if (ws == Node.SIGNAL) {  // 如果当前头结点的ws=signal,利用CAS把h的ws设为0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            
                    unparkSuccessor(h);  // 唤醒头结点的后继节点
                }   // 如果h的ws=0,就把h的ws设为PROPAGATE,表示可以向后传播唤醒
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                
            }
            if (h == head)  // 如果头结点没有发生改变,表示设置完成,可以退出循环
                break;      // 如果头结点发生了变化,可能被唤醒的其他节点重新设置了头结点
        }                   // 这样头结点发生了改变,要进行重试,保证可以传播唤醒信号
    }

同步状态的释放
通过tryReleaseShared方法获取返回值,如果返回值>=0,还是调用doReleaseShared方法去释放,释放成功直接返回true,释放后的同步状态独占式和共享式都能被唤醒尝试获取.

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {  // 
            doReleaseShared();
            return true;
        }
        return false;
    }

AQS主要实现了独占式和共享式,独占式无非就是同步状态在0与1之间切换,同一时刻只有一个线程获取锁进行操作,其他线程挂起,ReentrantLock就是个经典的独占式锁.共享式的PROPAGATE的数值>0,可以使同一时刻有多个线程获取锁,如果PROPAGATE<0,则就变成了共享模式,可以参照Semphore.

CAS

Compare and Swap,即比较再交换。CAS算法实现了区别于synchronouse同步锁的一种乐观锁。对CAS的理解,CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

CAS操作一般包含三个操作数——内存位置(V)、预期原值(A)和新值(B)
当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则
什么都不做并返回false

原理
(1)CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言
(2)借助C来调用CPU底层指令实现的
(3)当前的处理器基本都支持CAS,只不过不同的厂家的实现不一样
缺点
(1)开销大
并发量比较高的情况下,如果反复尝试更新某个变量,却又一直更新不成功,会给CPU带来较大的压力
(2)不能保证代码块的原子性只能保证一个变量的原子性操作,而多个方法或者代码块之的原子性则无法保证。
(3)ABA问题
当变量从A修改为B在修改回A时,变量值等于期望值A,但是无法判断是否修改,CAS操作在ABA修改后依然成功。
java.util.concurrent包为了解决这个问题,提供了一个带有标记的原子引用类"AtomicStampedReference",它可以通过控制变量值的版本来保证CAS的正确性。
不过目前来说这个类比较"鸡肋",大部分情况下ABA问题并不会影响程序并发的正确性,如果需要解决ABA问题,使用传统的互斥同步可能回避原子类更加高效。

ReentrantLock

ReetrantLock是Lock的主要实现类,ReetrantLock是一个可重入锁,同时可以指定公平锁和非公平锁
可重入锁
ReentrantLock是一个可重入锁,可重入锁是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生。

public static class TestReentrantLock {
    private Lock lock = new ReentrantLock();
    public void method() {
        lock.lock();
        try {
            System.out.println("方法1获得ReentrantLock锁");
            method2();
        } finally {
            lock.unlock();
        }
    }
    public void method2() {
        lock.lock();
        try {
            System.out.println("方法2重入ReentrantLock锁");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
            new TestReentrantLock().method();
    }
}

可重入锁的底层实现方式
ReentrantLock是使用AQS中的state的值,线程可以不停地lock来增加state的值,对应地需要unlock来解锁,直到state为零。并且state的值是用volatile进行修饰.

 //java.util.concurrent.locks.ReentrantLock.FairSync
 protected final boolean tryAcquire(int acquires) {
    //获取当前线程
    final Thread current = Thread.currentThread();
    int c = getState();
    //当前锁没被占用
    if (c == 0) {
        //1.判断同步队列中是否有节点在等待
       if (!hasQueuedPredecessors() &&
           compareAndSetState(0, acquires)) {//2.如果上面!1成立,修改state值(表明当前锁已被占用)
            //3.如果2成立,修改当前占用锁的线程为当前线程
           setExclusiveOwnerThread(current);
           return true;
       }
    }
    //占用锁线程==当前线程(重入)
    else if (current == getExclusiveOwnerThread()) {
       int nextc = c + acquires;//
       if (nextc < 0)
           throw new Error("Maximum lock count exceeded");
        //修改status
       setState(nextc);
       return true;
    }
    //直接获取锁失败
    return false;
}


公平锁和非公平锁
ReentrantLock可以实现公平锁和非公平锁,所谓的公平锁,就是在多个线程请求获取同一个资源的时候,能够保证依照线程请求的顺序,依次执行,来保证公平竞争的效果。反之,非公平锁就是不按照线程请求顺序,每个线程一起去争抢锁,谁抢到是谁的。
lock() 方法获取锁的主要流程
ReentrantLock 在 lock() 方法中调用了其成员变量 sync 的 lock() 方法

public void lock() {
    sync.lock();
}

在 NonfairSync 和 FairSync 两个 Sync 的子类中,覆盖了父类的 lock() 方法,并都会调用 Sync 父类 AbstractQueuedSynchronizer (AQS) 中的 acquire() 方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

unlock() 方法释放锁的主要流程
ReentrantLock 在 unlock() 方法中调用了其成员变量 sync 的 release(1) 方法,即调用的是 AQS 的 release(int arg) 方法

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

AQS 的 release(int arg) 方法

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

AQS 的 release(int arg) 方法

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

AQS 的 release(int arg) 方法中会先调用 tryRelease(arg) 方法,即调用了 Sync 自身的 tryRelease(int releases) 方法

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

非公平锁
NonfairSync 继承自 Sync,并覆盖了 Sync 的 lock() 方法,同时还覆盖了 AbstractQueuedSynchronizer 的 tryAcquire() 方法

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

AQS 中维护了一个锁状态字段 state,由 volatile 进行修饰,其中 0 表示未被获取,1 表示已被获取, 大于 1 表示重入数
当调用 compareAndSetState(0, 1) 方法来获取锁时,即尝试将锁状态 state 从 0 变为 1,若操作成功,则其他线程调用 compareAndSetState(0, 1) 方法时就会失败,直到当前线程释放锁为止
CAS 操作失败,则会调用 AQS 的 acquire(1) 方法,即调用了 NonfairSync 自身的 tryAcquire(int acquires) 方法来获取独占锁,若获取失败,则会调用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 方法将请求获取锁的线程加入到等待队列中等待,直到其他线程释放锁,由请求获取锁的线程获取并返回

tryAcquire(int acquires) 方法中又调用了 Sync 的 nonfairTryAcquire(int acquires) 方法,

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        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;
}

如果锁状态 (state) 为 0 时,会进行 CAS 操作来获取独占锁,若操作成功,则将当前线程设置为该锁的唯一拥有者并返回 true;如果持有锁的线程为当前拥有独占锁的线程,则通过累加状态标识 nextc 来记录重入次数,并返回 true;若都不是,则返回 false
公平锁
FairSync 继承自 Sync,并覆盖了 Sync 的 lock() 方法,同时还覆盖了 AbstractQueuedSynchronizer 的 tryAcquire() 方法

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;

    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;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
}

FairSync 的 lock() 方法中,仅调用了 AQS 的 acquire(1) 方法,即调用了 FairSync 自身的 tryAcquire(int acquires) 方法来获取独占锁,若获取失败,则会调用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) 方法将请求获取锁的线程加入到等待队列中
tryAcquire(int acquires) 方法中,如果锁状态 (state) 为 0 时,会先通过 hasQueuedPredecessors() 方法来判断等待队列中有没有比当前线程等待时间更长的线程 (即优先级更高),如果没有,则再进行 CAS 操作来获取独占锁,若操作成功,则将当前线程设置为该锁的唯一拥有者并返回 true;如果持有锁的线程为当前拥有独占锁的线程,则通过累加状态标识 nextc 来记录重入次数,并返回 true;若都不是,则返回 false

public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    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());
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值