AQS 公平锁 非公平锁

AQS

java.util.concurrent.locks.AbstractQueuedSynchronizer,译为抽象队列式同步器
AQS 提供了原子式管理同步状态、阻塞和唤醒线程功能以及等待队列模型的简单框架;

AQS 包含了一个虚拟的Node双向链表(即等待队列),由 volatile 修饰的头&尾节点,以及同步状态标志state,节点等待状态标志waitStatus,和当前线程信息

AQS 提供了共享锁与独占锁的支持

独占锁

同一个时刻只能被一个线程占有,如ReentrantLock,ReentrantWriteLock等,其中又包含了
公平锁与非公平锁

共享锁

同一时间点可以被多个线程同时占有,如ReentrantReadLock,Semaphore等

公平锁 & 非公平锁

初始化

    // 公平锁
    ReentrantLock lock = new ReentrantLock(true);
    // 非公平锁
    ReentrantLock lock = new ReentrantLock();
    ReentrantLock lock = new ReentrantLock(false);

加锁过程

java.util.concurrent.locks.ReentrantLock.FairSync#lock

    final void lock() {
        // 尝试将当前锁的状态置为1 即为加锁成功
        acquire(1);
    }

    public final void acquire(int arg) {
    /**
     * 尝试加锁 tryAcquire = true 则加锁成功
     * 加锁失败时进入后续的等待流程 acquireQueued
     * acquireQueued 将无限循环获取锁
     * 若获取到锁直接开始执行 
     * 或前节点成为头部 自己即将执行 则暂时挂起
     */ 
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
    }
1.尝试加锁(公平锁模式)
    /**
     * 尝试获取锁 设置锁状态 锁状态的数值实际为获取锁线程的加锁的次数(可重入)
     * 返回true为加锁成功 false为失败
     */
    protected final boolean tryAcquire(int acquires) {
        // 当前线程
        final Thread current = Thread.currentThread();
        // 获取当前锁的状态
        int c = getState();
        // 当前锁状态为0 则为可以加锁
        if (c == 0) {
            /**
             * 非公平锁模式将不会判断队列是否有其他节点 会直接尝试获取锁
             * hasQueuedPredecessors = false 
             * 则等待队列中没有其他线程 
             * 即当前线程是下一个应当获取锁的线程
             */ 
            if (!hasQueuedPredecessors() && 
                /**
                 * 考虑到有其他线程同时判断完毕
                 * 故进行CAS操作 尝试修改锁状态
                 */ 
                compareAndSetState(0, acquires)) {
                // 前两步都已成功 则将当前线程设置为持有锁的线程
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        // 当前锁状态不为0 且持有锁的线程是当前线程 则直接重入
        else if (current == getExclusiveOwnerThread()) {
            // 设置当前锁状态+1 即当前线程总共上锁的次数
            int nextc = c + acquires;
            // 若上锁次数为负 实为上锁次数越界
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return 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());
}
CAS操作进行锁状态变更
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2.加锁失败 进入等待队列

/**
 * 将当前线程构建为等待队列的元素 即Node 
 * 下文将用Node表示当前线程构筑的等待队列元素
 * 将该Node等待的线程设置为当前锁的持有线程
 * CAS操作 尝试将当前Node 设置为队列尾部元素
 */ 
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 的前节点元素
        node.prev = pred;
        // 尝试将当前线程Node设置成尾部元素
        if (compareAndSetTail(pred, node)) {
            // 成功后 修改旧的队尾元素的后一个节点为当前Node
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}    

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        // 当前Node会在此处无限循环 直到前节点成为头部 自己获取到锁才返回
        for (;;) {
            // 获取当前Node的前节点
            final Node p = node.predecessor();
            // 若当前节点的前节点已成为队列头部 则再次尝试加锁
            if (p == head && tryAcquire(arg)) {
                // 成功后 设置当前Node为头部元素
                setHead(node);
                p.next = null; // help GC
                // 此时已获取到锁 故无需再设置自己为中断状态 
                // 进入队列结果则为失败
                failed = false;
                return interrupted;
            }
            /**
             * shouldParkAfterFailedAcquire 判断当前线程是否能够挂起
             * 清除队列中已取消的节点
             * 判断为true 则可以挂起
             */ 
            if (shouldParkAfterFailedAcquire(p, node) &&
                /**
                 * parkAndCheckInterrupt 执行挂起
                 * 并返回当前线程是否有过中断请求
                 */
                parkAndCheckInterrupt())
                /**
                 * 当前线程在等待过程中无法响应中断 直到获取到锁
                 * 如果在整个等待过程中被中断过 则interrupted = true
                 * acquireQueued最终返回true 否则返回false 
                 * 并在外层响应中断
                 * 需要其他线程调用当前线程的interrupt()方法
                 */
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

参考资料:
https://blog.csdn.net/Java_zh...
https://blog.csdn.net/Leon_Ji...
https://blog.csdn.net/yy_dieg...

你可能感兴趣的:(java)