AbstractQueuedSynchronizer原理解析

990 阅读21分钟

1、初次相识

在ReentrantLock锁代码中,你发现,锁的动作是由Sync的实例来处理,而Sync是继承了AbstractQueuedSynchronizer;而在ReentrantReadWriteLock锁中也会由这样的发现;没错,AbstractQueuedSynchronizer是锁实现的基石,实现了资源相关线程出队入队、唤醒、暂停等逻辑;通过这些逻辑,你可以实现可重入锁,共享锁,独占锁,条件锁,公平锁,非公平锁(条件锁,不是一种锁,是一种使用方法)。

2、原理

锁其实是对一些资源的保护,拥有这些资源,可以继续执行,不持有资源执行权限则等待资源被释放后,再次竞争资源的执行权限;我在本文中也习惯,把线程可以继续执行,看成是持有资源执行权限,若被挂起,则表明未持有资源,且资源被其它线程持有,若果被唤醒,则需要继续竞争资源的执行权限

其内部实现有一些重要的类,我们先讲讲,然后再了解具体实现

2.1 Unsafe类

Unsafe类是在sun.misc包下,不属于Java标准,android中也不是标准支持,可以通过使用java库的方式+反射调用,另外可以用LockSupport类+Atomic原子类操作来代替Unsafe类的功能,其时CAS操作的基石;Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,一旦能够直接操作内存,因此操作时,需要对象地址,对象成员变量相对地址偏移量,旧值,要修改后的值;

两个方面的用途

  • 线程的停止、唤醒
  • 基础数据、对象的写,具有原子性

其使用如下:

这时AtomicInteger的代码:首先静态获取变量相对类的偏移量,在对象处理时,使用当前对象地址+偏移量,推算出当前变量地址

    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    private static final long VALUE;

    static {
        try {
            VALUE = U.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
    }

    private volatile int value;
    
    public final boolean compareAndSet(int expect, int update) {
        return U.compareAndSwapInt(this, VALUE, expect, update);
    }

代码解读:

  1. VALUE 为value变量在AtmicInteger实例地址的相对偏移量,这个相对偏移量对于类,在类加载后就是一定的
  2. U提供了设置方法,但是为了保证读写操作的原子性,我们一般选择使用volatile+和旧值比对然后替换的方法来达到原子性
  3. 和旧值比对,如果一致,替换才能成功,否则不能成功

原子性的原理:

  • 读的结果为最新
  • 写的时候,旧值符合预期才能成功,如果变动了则旧值旧变化了,也就不能写成功了 存在问题ABA问题,也就是旧值在一个线程变为A,让后在另外一个线程变为B,某个线程为A;你当时获取为A,那么也是可以修改为你想要的了;这个问题是添加时间戳来记录旧值并比对

2.2 静态内部Node类

    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;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;
        
        final boolean isShared() {
            return nextWaiter == SHARED;
        }
        
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {}

        Node(Node nextWaiter) {
            this.nextWaiter = nextWaiter;
            U.putObject(this, THREAD, Thread.currentThread());
        }

        Node(int waitStatus) {
            U.putInt(this, WAITSTATUS, waitStatus);
            U.putObject(this, THREAD, Thread.currentThread());
        }

        final boolean compareAndSetWaitStatus(int expect, int update) {
            return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
        }

        final boolean compareAndSetNext(Node expect, Node update) {
            return U.compareAndSwapObject(this, NEXT, expect, update);
        }

        private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
        private static final long NEXT;
        static final long PREV;
        private static final long THREAD;
        private static final long WAITSTATUS;
        static {
            try {
                NEXT = U.objectFieldOffset
                    (Node.class.getDeclaredField("next"));
                PREV = U.objectFieldOffset
                    (Node.class.getDeclaredField("prev"));
                THREAD = U.objectFieldOffset
                    (Node.class.getDeclaredField("thread"));
                WAITSTATUS = U.objectFieldOffset
                    (Node.class.getDeclaredField("waitStatus"));
            } catch (ReflectiveOperationException e) {
                throw new Error(e);
            }
        }
    }

主要内容有:

  • 实现了next、prev、thread、waitStatus四个内部变量的CAS操作
  • 双向链表结构的节点
  • waitStatus变量标志线程thread的状态

2.3 条件内部类ConditionObject

条件锁的队头队尾,也是先进先出队列,并且其利用nextWaiter指向下一个条件节点,也就是单向链表

private transient Node firstWaiter;
private transient Node lastWaiter;

2.3.1 条件锁等待流程

   public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) 
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
  • addConditionWaiter方法,进行加入条件队列
  • fullyRelease方法:由于当前线程持有资源,才可以使用条件等待,所以需要先释放资源,让其它线程唤醒执行,才可以使用资源
  • isOnSyncQueue方法,查看是否在锁的等待队列(外部类的队列,包含独占锁、共享锁的对象的那个),不在会被挂起;
  • checkInterruptWhileWaiting方法,如果处于打断状态,则有可能把节点加入锁等待队列
  • acquireQueued:尝试获取资源,若未获取,则挂起线程,否则,继续执行
  • unlinkCancelledWaiters方法,条件队列重新检查,去除无效节点,被取消的线程节点
  • reportInterruptAfterWait 方法,设置线程打断

2.3.2 条件锁唤醒流程

 public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }
  • isHeldExclusively方法直接抛出异常,需要自定义实现
  • doSignalAll方法释放每一个节点

doSignalAll方法

        private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }

其具体释放动作由transferForSignal实现

transferForSignal方法

   final boolean transferForSignal(Node node) {
        if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
            return false;
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
  • 假设当前状态为条件状态,重置为运行状态,如果失败,则false,也即是进行唤醒下一个,尝试去获取资源:也就是这是状态已经被改变了
  • 如果状态修改为运行状态,节点加入到锁等待队列;再次判断状态,如果已经取消,或者重置状态为等待唤醒失败,则唤醒线程;说明线程要么取消,要么状态已经改变,CAS置换为等待唤醒状态失败,则当前线程不能被唤醒(详细看下方独占锁释放时的判断),所以需要在这里释放

小结

  • 条件锁,有专门的队列,存放条件等待线程节点;单向列表,使用nextWaiter指向下一个
  • isHeldExclusively方法需要实现,表示是否为当前线程持有锁
  • 条件锁释放后,条件等待队列中节点如果不是条件状态,说明已经被改变,也就是被唤醒
  • 如果仍是condition状态,则加入锁等待队列,并重新检查状态,如果取消或者CAS重置状态失败,则唤醒线程

2.4 CAS操作内部变量

    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
    private static final long STATE;
    private static final long HEAD;
    private static final long TAIL;

    static {
        try {
            STATE = U.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            HEAD = U.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            TAIL = U.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
        } catch (ReflectiveOperationException e) {
            throw new Error(e);
        }
        Class<?> ensureLoaded = LockSupport.class;
    }

原子操作的对象:状态state,队列头head,队列尾tail 内部变量state实现了原子非原子操作

    private volatile int state;
    protected final int getState() {
        return state;
    }

    protected final void setState(int newState) {
        state = newState;
    }

    protected final boolean compareAndSetState(int expect, int update) {
        return U.compareAndSwapInt(this, STATE, expect, update);
    }

内部变量head,也是实现了非原子操作

    private transient volatile Node head;
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }
    
    private final void initializeSyncQueue() {
        Node h;
        if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
            tail = h;
    }

内部变量tail,也是实现了非原子操作

   private transient volatile Node tail;
   private final void initializeSyncQueue() {
        Node h;
        if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
            tail = h;
    }

    private final boolean compareAndSetTail(Node expect, Node update) {
        return U.compareAndSwapObject(this, TAIL, expect, update);
    }

同样在内不对于head,tail变量还有直接使用的,也是不满足原子操作的,那么为何最后又能达到同步的效果呢,只能看具体代码了

2.5 独占锁实现细节

ReentrantLock是独占锁,而且内部可以是公平锁,非公平锁; 公平锁:

        final void lock() {
            acquire(1);
        }

非公平锁:

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

由此可见:

  • 当state为0时,资源处于空闲状态, 其它线程可以获取资源的执行权限
  • 当state为1时,资源已经被线程持有,其它线程再使用资源时需要排队等待资源被释放
  • 所谓非公平锁,就是如果资源是未被持有,那么正在请求获取锁的线程,比等待队列中的锁优先
  • 获取资源执行权限,调用同步器的acquire方法;结果可能未持有资源,线程则被挂起

2.5.1 上锁流程

acquire方法

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

涉及简单方法分析:

  • tryAcquire方法直接抛出异常,也即是自定义独占锁必须实现这个方法;
  • selfInterrupt 获取锁的线程进行中断操作,这个并不一定导致线程停止

addWaiter方法

整体来说,就是加入一个节点到队列尾部;如果未初始化队列,则进行初始化(延时策略)

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

        for (;;) {
            Node oldTail = tail;
            if (oldTail != null) {
                U.putObject(node, Node.PREV, oldTail);
                if (compareAndSetTail(oldTail, node)) {
                    oldTail.next = node;
                    return node;
                }
            } else {
                initializeSyncQueue();
            }
        }
    }
  1. 首先生成一个Node节点,这个节点nextWaiter为空(Node.EXCLUSIVE为空对象);独占锁的nextWaiter为空
  2. for循环自旋
  • 如果队列未进行初始化,则initializeSyncQueue进行初始化,如果不成功,继续此步骤直至成功
  • 加入队列尾部

acquireQueued方法

    final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }
  • for循环自旋;如果其是头节点下的第一个节点,如果尝试获取资源成功,则进行设置为队列头,释放之前头节点,并返回false;返回false,则意味线程跳出自旋,可以继续执行
  • 如果不是锁等待队列的第二个,则执行shouldParkAfterFailedAcquire方法,如果为true,继续执行parkAndCheckInterrupt方法
  • shouldParkAfterFailedAcquire方法执行后,返回false会去掉取消的节点,之后如果未有状态变化(比如外部取消线程,打断等操作),则会返回true,可以详细看下面方法源码

shouldParkAfterFailedAcquire方法

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }
  • 如果node 前一个节点pred节点已经是等待唤醒状态,则返回true,表示当线线程应该被暂停
  • 如果node前一个节点状态大于0,暂时好像只有取消状态的,则找到一个状态小于等于0的,并是node为其后继节点,则寻找过程中的节点都会被移除队列,返回false
  • 如果node前一个节点已经是小于等于0了,这时把前一个几点的状态置为等待唤醒-1,返回false

parkAndCheckInterrupt方法

   private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
  • 暂停正在执行的线程,并返回打断状态

2.5.2 释放锁流程

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  • tryRelease需要自定义实现,否则直接抛出异常
  • 如果头节点不是正在运行状态,则解锁头节点线程,释放锁成功
  • 否则不需要释放锁,或者释放失败(按照正常,在锁等待队列中,独占锁/条件锁,都会为signal状态,为共享锁为signal或者PROPAGATE状态)

unparkSuccessor方法

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            node.compareAndSetWaitStatus(ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
  • CAS操作,释放头节点状态设置为0
  • 从头结点后寻找一个节点不为空,且节点为0的节点,释放此节点的线程(上述获取资源时,进行自旋,去除头结点后的取消的节点后的第一个节点才可以获取资源)

2.5.3 独占锁原理小结

  • nextWaiter为空
  • 排队等锁的队列,头优先获取资源(对于非公平锁,新获取未排队的线程也会获取锁);尝试获取资源的线程排队到队尾
  • 获取资源失败的线程,被挂起;持有线程执行完毕,则头节点的下一个节点恢复执行,尝试获取资源(非公平锁,会和新获取锁未排队进来的线程争夺锁)成功后继续执行其任务,失败线程挂起,并置为等待唤醒状态
  • 自定义独占锁需要实现tryRelease、tryAcquire方法
  • 可重入锁,即当前线程再次获取资源,状态+1,释放资源状态-1,如果是0,则是当前线程完全释放了资源,其它排队线程可以获取资源了;代码如下:非公平锁的代码
   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;
        }

        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;
        }

2.6 共享锁实现细节

参照ReentrantReadWriteLock的读锁为共享锁,写锁是独占锁

获取锁

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

释放锁

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

2.6.1 获取锁流程

    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
  • tryAcquireShared 方法直接抛出异常,需要自定义实现
  • tryAcquireShared 方法返回大于等于0时,则继续进行;小于0则获取锁失败

doAcquireShared方法

    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        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();
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }
  • 新建一个节点,节点的nextWaiter值为Node.SHARED,并加入队尾
  • 如果其前驱节点,不是头节点,则暂停当前线程(方法分析,见独享锁)
  • 如果前驱节点是头节点:尝试获取锁失败,也会暂停线程,如果成功则会执行方法setHeadAndPropagate,并返回true

setHeadAndPropagate方法

    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head;
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
  • 设置节点为HEAD节点
  • 满足条件的话,唤醒下一个节点线程,满足下列之一:1)之前头节点为空,2)之前的线程状态小于0,3)现在的head节点为null或者节点相关线程状态小于0;并且需要满足node节点下一个节点不为空或者是共享节点

doReleaseShared方法

   private void doReleaseShared() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                        continue; 
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                    continue;
            }
            if (h == head)
                break;
        }
    }

自旋释放线程

  • 头节点为空,或者与尾节点相同,则当前没有需要唤醒的线程,直接退出
  • 如果头节点状态为待唤醒状态,则置换状态,唤醒线程(之后会接着尝试获取共享资源,接着释放后续共享资源节点。。。),结束自旋
  • 如果头节点的状态正在运行,则CAS置换为-3失败,则重复流程
  • CAS置换状态为-3,则结束

2.6.2 释放锁流程

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }
  • tryReleaseShared直接抛出异常,需要自定义实现
  • tryReleaseShared为true,则进行释放共享资源,并返回释放成功

2.6.3 共享锁原理小结

  • nextWaiter为Node.SHARED
  • 排队等锁的队列,头优先获取锁(对于非公平锁,新获取未排队的线程也会获取锁);尝试获取锁的线程排队到队尾
  • 共享节点获取资源成功,依次唤醒相邻的共享资源节点;如果被唤醒的相邻节点再次执行tryAcquireShared不能获取执行权限,则再次被挂起
  • 自定义共享锁需要实现tryReleaseShared、tryAcquireShared方法

3、同步器原理实现总结

  • 实现独占锁,需要实现方法tryRelease、tryAcquire,isHeldExclusively方法
  • 实现共享锁时,需要实现,tryReleaseShared、tryAcquireShared,isHeldExclusively方法;
  • tryAcquire,tryAcquireShared结果返回true时,表示持有锁,则继续执行
  • 使用条件锁时,条件队列内部竞争后,还会和锁队列竞争
  • 锁通过state来标志是否需要等待,0无需等待,1表示锁被持有,> 1表示锁被持有且是可重入锁;
  • 线程持有锁时,使用setExclusiveOwnerThread设置持有线程,使用getExclusiveOwnerThread获取持有线程

4、锁的实现

4.1 ReentrantLock锁

4.1.1 静态内部抽象类类Sync

并不是每个方法都进行分析,详情如下

  • 重点方法nonfairTryAcquire,tryRelease和lock抽象方法
  • isHeldExclusively方法实现,判断持有锁线程是否当前线程
  • newCondition方法利用内部类,生成条件锁对象
  • getOwner方法,获取持有锁线程对象
  • getHoldCount方法获取当前线程持有锁重入次数
  • isLocked方法判断已有线程持有锁
  • readObject方法,序列化操作

一些特殊变量

private transient Thread firstReader;
private transient int firstReaderHoldCount;

共享锁第一个线程对象,和第一个线程对象重入次数

private transient ThreadLocalHoldCounter readHolds;

ThreadLoack对象,key为线程,value为HoldCounter对象(包含重入次数,线程tid)

private transient HoldCounter cachedHoldCounter;

缓存对象,上次共享锁尝试获取时,线程信息

nonfairTryAcquire方法

        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;
        }
  • 如果当前未有线程持有资源(锁等待队列为空,或者持有资源线程刚释放锁),CAS操作设置状态,如果成功,则设置当前线程为持有资源线程, 返回true;不公平有这个方面的原因,尝试获取资源的新线程,直接和锁等待队列表头竞争锁,失败后才放在锁等待队列队尾,也即是有可能比它先等锁的线程先执行
  • 当前线程即为持有资源线程,状态+1,返回true;可重入性
  • 其它情况,均未获取资源,进入锁等待队列

tryRelease方法

        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;
        }
  • 首先判断当前线程 == 持有资源的线程,否则异常
  • 如果状态减1,之后为0,则设置持有线程为null,返回true,表示资源可以被锁等待队列节点竞争
  • 如果不为0,返回false,这时当前锁为可重入锁,线程持有资源的次数未进行完全释放,不需要去唤醒锁队列的线程

4.1.2 公平锁

    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;
        }
    }
  • 获取锁时,直接调用acquire获取锁,acquire是共享锁获取的直接入口,也说明,默认实现也就是公平锁
  • hasQueuedPredecessors方法:true表示:锁队列头!=尾,头的下个节点为空或者头的下个节点线程!=当前线程
  • 如果队列为空;进行原子改变状态操作,并设置持有锁线程,返回true;
  • 如果当前线程就是持有资源的线程,则,状态+1,返回true;可重入性
  • 其它情况均不能直接获取资源,

4.1.3 非公平锁

   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);
        }
    }
  • 尝试获取锁采用非公平获取方式nonfairTryAcquire方法,见4.1.1 Sync对象分析
  • 尝试获取锁时,直接CAS操作当前状态,尝试从旧状态0设置为1,如果成功,则直接持有锁

4.2 ReentrantReadWriteLock读写锁

    private final ReentrantReadWriteLock.ReadLock readerLock;
    private final ReentrantReadWriteLock.WriteLock writerLock;
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

读锁对象、写锁对象都是静态内部抽象类Sync对象来代理实现的

4.2.1 静态内部抽象类Sync

读写锁数量

        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
  • 数据32位,读锁高位16位,写锁低位16位
  • 读锁个数增加1个,则数据增加SHARED_UNIT,写锁个数增加1则数据就是+1

抽象方法

        abstract boolean readerShouldBlock();
        abstract boolean writerShouldBlock();

读写锁阻塞策略

tryAcquire方法

        protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

按照下面流程进行处理

  1. 资源已经被线程持有,独占锁个数为0或者当前线程不为持有线程,尝试获取资源失败
  2. 资源已经被线程持有,独占锁个数超标,抛异常
  3. 资源已经被线程持有,则尝试获取锁成功,state+1
  4. 资源未被持有,如果写被阻塞或者状态CAS操作失败,尝试获取锁失败
  5. 否则,直接获取锁成功,并设置当前线程为执行线程

tryRelease方法

        protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }
  1. 检查释放锁线程是否为当前线程,不是,则抛出异常
  2. 如果当前state为0,则设置执行线程为空
  3. 设置当前状态,以state == 0 为返回结果

tryAcquireShared方法

        protected final int tryAcquireShared(int unused) {
            Thread current = Thread.currentThread();
            int c = getState();
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    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;
            }
            return fullTryAcquireShared(current);
        }

代码执行流程如下:

  1. 如果独占锁线程个数不为0且当前线程不为执行线程,则返回-1
  2. 如果共享锁不被阻塞且共享锁个数没有达到最大,锁状态CAS操作成功,返回1;其中计算线程重入次数并记录,如果第一个共享锁线程,则使用firstReader,firstReaderHoldCount,记录线程对象,个数;否则使用readHolds记录线程tid和重入次数信息,重置缓存信息cachedHoldCounter
  3. 返回 fullTryAcquireShared方法执行结果

fullTryAcquireShared方法

        final int fullTryAcquireShared(Thread current) {
            HoldCounter rh = null;
            for (;;) {
                int c = getState();
                if (exclusiveCount(c) != 0) {
                    if (getExclusiveOwnerThread() != current)
                        return -1;
                } else if (readerShouldBlock()) {
                    if (firstReader == current) {
                    } else {
                        if (rh == null) {
                            rh = cachedHoldCounter;
                            if (rh == null || rh.tid != getThreadId(current)) {
                                rh = readHolds.get();
                                if (rh.count == 0)
                                    readHolds.remove();
                            }
                        }
                        if (rh.count == 0)
                            return -1;
                    }
                }
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {
                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh;
                    }
                    return 1;
                }
            }
        }

for循环进行自旋,流程如下

  1. 独占锁线程个数不为0且当前线程不为执行线程,则返回-1
  2. 独占锁个数为0,共享锁被阻塞策略,获取当前线程的重入次数,若为0则readHolds对象中去除,并返回-1
  3. 共享锁个数超过最大值,抛出异常
  4. CAS操作状态,成功,则返回1;另外,计算线程重入次数并记录,如果第一个共享锁线程,则使用firstReader,firstReaderHoldCount,记录线程对象,个数;否则使用readHolds记录线程tid和重入次数信息,重置缓存信息cachedHoldCounter

tryReleaseShared方法

        protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firszhitReader == current) {
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }

处理一下流程

  1. 当前线程为第一个共享,若共享次数为1,则置firszhitReader为null,否则firstReaderHoldCount减1
  2. 非第一个共享线程,若此线程重入次数为1,则从readHolds删除,否则其重入次数减1
  3. for循环自旋,CAS重置state状态,成功,则返回重置后状态是否为0

4.2.2 静态内部类NonfairSync

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false;
        }
        final boolean readerShouldBlock() {
            return apparentlyFirstQueuedIsExclusive();
        }
    }
    
    
    final boolean apparentlyFirstQueuedIsExclusive() {
        Node h, s;
        return (h = head) != null &&
            (s = h.next)  != null &&
            !s.isShared()         &&
            s.thread != null;
    }
  • 写锁无条件不被阻塞,而且写锁可以获取资源执行的可能时,读锁被阻塞;写锁必读锁有更高的优先级
  • apparentlyFirstQueuedIsExclusive方法,AQS中方法,不应阻塞情况
  1. 队列为空
  2. 头节点的后继节点为空
  3. 头节点后继节点是共享节点
  4. 头节点后继节点的线程对象为空

4.2.3 静态内部类FairSync

   static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

是不是应该阻塞获取锁,都是判断是否有前驱节点,有前驱节点说明不是头节点,则不应该持有锁;

4.2.5读锁入口

        public void lock() {
            sync.acquireShared(1);
        }
        public void unlock() {
            sync.releaseShared(1);
        }

使用基类的共享锁的获取和释放流程

4.2.5 写锁入口

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

使用基类的独占锁的获取和释放流程

技术变化都很快,但基础技术、理论知识永远都是那些;作者希望在余后的生活中,对常用技术点进行基础知识分享;如果你觉得文章写的不错,请给与关注和点赞;如果文章存在错误,也请多多指教!