【Java并发专题之五】juc-locks锁框架之AQS

AQS(AbstractQueuedSynchronizer)框架提供了一套通用的机制来管理同步状态、阻塞/唤醒线程、管理同步队列。

一、同步机制
AQS框架描述了一个什么样的锁?

一个公司有10个人,这个公司只有一个厕所,而且是一个单人使用的厕所,厕所有门,门上有一个显示器:
当厕所无人使用时,显示器显示:
    厕所状态:空闲中
    使用人:无

当小明上厕所时,显示器显示:
    厕所状态:使用中
    使用人:小明

当小强上厕所时,显示器显示:
    厕所状态:使用中
    使用人:小强
...

1、锁的使用人
AbstractOwnableSynchronizer父类里定义了一个字段-拥有锁的线程,以及相应的set、get方法,相当于上面厕所使用人:

private transient Thread exclusiveOwnerThread;

protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}

protected final Thread getExclusiveOwnerThread() {
    return exclusiveOwnerThread;
}

2、锁状态
AbstractOwnableSynchronizer的子类AbstractQueuedSynchronizer里定义了锁状态,相当于上面的厕所的厕所状态:

/**同步状态 锁状态*/
private volatile int state;

/**获取State*/
protected final int getState() {
    return state;
}

/**设置State*/
protected final void setState(int newState) {
    state = newState;
}

/**
 * CAS修改状态:当前值和期望值相同时 原子性的修改更新值
 * 返回true说明修改成功  返回false说明当前值和期望值不相同
 */
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

锁就是线程竞争的资源,AQS使用int变量state来表示这个资源状态(同步状态),这与synchronize内部锁(底层锁mutex)是不一样的,这里叫同步状态比锁更合适一些。
独占模式:比如ReentrantLock,初始state=0,某一个线程获取到同步状态state+1,释放同步状态state-1;
共享模式:比如Semaphore/CountDownLatch,多个线程都会获取到同步状态;

二、内部类Node
Node类被设计作为同步队列(CLH)和条件队列(Condition)的节点,条件队列只限独占模式下使用。

源码

static final class Node {
        /** 静态属性:标记节点为共享模式*/
        static final Node SHARED = new Node();
        /** 静态属性:标记节点为独占模式*/
        static final Node EXCLUSIVE = null;
        /** 静态属性:waitStatus的值 表明线程已取消 当前线程因为超时或者中断被取消。这是一个终结态,也就是状态到此为止。*/
        static final int CANCELLED =  1;
        /** 静态属性:waitStatus的值  表明后继节点中的线程需要唤醒 用于独占锁
         * 当前线程的后继线程被阻塞或者即将被阻塞,当前线程释放锁或者取消后需要唤醒后继线程。这个状态一般都是后继线程来设置前驱节点的。
         */
        static final int SIGNAL    = -1;
        /** 静态属性:waitStatus的值 当前节点需要进入等待队列 用于独占锁*/
        static final int CONDITION = -2;
        /**静态属性:waitStatus的值 下一个acquireShared方法被允许 用于共享锁
         * 用于将唤醒后继线程传递下去,这个状态的引入是为了完善和增强共享锁的唤醒机制。在一个节点成为头节点之前,是不会跃迁为此状态的
         */
        static final int PROPAGATE = -3;
        /**
         * Status field, taking on only the values:
         *   SIGNAL:     发信号。表示后继结点被阻塞了(当前结点在入队后、阻塞前,应确保将其prev结点类型改为SIGNAL,以便prev结点取消或释放时将当前结点唤醒。)
         *   CANCELLED:  取消。表示后继结点被中断或超时,需要移出队列
         *   CONDITION:  节点处于等待队列 当转向同步队列时 将改为0
         *   PROPAGATE:  传播
         *   0:          初始状态
         *   初始化状态0用于同步队列;初始化-2用于等待队列,并通过CAS方法来修改
         */
        volatile int waitStatus;

        /**当前节点的前一个节点,用于维持同步队列(CLH)*/
        volatile Node prev;

        /**当前节点的下一个节点,用于维持同步队列(CLH)*/
        volatile Node next;

        /**当前节点包裹的线程*/
        volatile Thread thread;

        /**
         * 如果是共享模式:nextWaiter=SHARED
         * 如果是独占模式:nextWaiter为等待队列的下一个节点
         */
        Node nextWaiter;

        /** 根据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() {    // Used to establish initial head or SHARED marker
        }
        /**用在addWaiter方法里 向CLH队列添加新节点的构造器*/
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
        /**用在Condition里向等待队列添加新节点的构造器*/
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

1、模式

2、状态


3、4个链接引用

猜你喜欢

转载自www.cnblogs.com/cac2020/p/12098693.html
今日推荐