AQS-AbstractQueuedSynchronizer

AQS概述

结合下面的类图,可以知道AQS可以理解为同步器Sync的一部分,线程加锁,释放锁的逻辑均是在这里面进行的,是队列中非常重要的一个类。

实现原理

在这里插入图片描述
由上图我们可以清楚的看出,ReentrantLock,WriteLock,ReadLock中均有一个同步器Sync,当然不仅仅是这3个中有同步器,CountDownLatch,Semaphore其中也都有同步器Sync。

同时,由图中可以看出,同步器Sync的父类AbstractQueuedSynchronizer是一个抽象类,Sync中很多的方法都是调用父类AQS中的方法进行实现的。

所以接下来了,借助,Sync的使用来看下AQS到底是怎样进行工作的。

1、AQS属性,构造器
1.1 属性
	// 表示队列的头节点
    private transient volatile AbstractQueuedSynchronizer.Node head;
    // 表示队列的尾部节点
    private transient volatile AbstractQueuedSynchronizer.Node tail;
    // 表示当前先线程是否获取到了锁。0:未获取锁  非0 :获取到了锁
    private volatile int state;
    static final long spinForTimeoutThreshold = 1000L;
    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;
1.2 构造器
    protected AbstractQueuedSynchronizer() {
    }

AQS只有一个无参的构造器。

1.3 内部类
    static final class Node {
    	// 共享模式
        static final AbstractQueuedSynchronizer.Node SHARED = new AbstractQueuedSynchronizer.Node();
        // 独占模式
        static final AbstractQueuedSynchronizer.Node EXCLUSIVE = null;
        // waitStatus 被中断
        static final int CANCELLED = 1;
        // waitStatus 该线程被唤醒
        static final int SIGNAL = -1;
        // waitStatus 该线程处于wiating状态
        static final int CONDITION = -2;
        // waitStatus 传播
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        // 当前节点的前一个节点
        volatile AbstractQueuedSynchronizer.Node prev;
        // 当前节点的下一个节点
        volatile AbstractQueuedSynchronizer.Node next;
        // 当前节点对应的线程
        volatile Thread thread;
        AbstractQueuedSynchronizer.Node nextWaiter;

        final boolean isShared() {
            return this.nextWaiter == SHARED;
        }

        final AbstractQueuedSynchronizer.Node predecessor() throws NullPointerException {
            AbstractQueuedSynchronizer.Node var1 = this.prev;
            if (var1 == null) {
                throw new NullPointerException();
            } else {
                return var1;
            }
        }

        Node() {
        }

        Node(Thread var1, AbstractQueuedSynchronizer.Node var2) {
            this.nextWaiter = var2;
            this.thread = var1;
        }

        Node(Thread var1, int var2) {
            this.waitStatus = var2;
            this.thread = var1;
        }
    }

#### 2、同步器 由上图,我们可以看出,同步器Sync的实现有公平锁和非公平锁2种。 - 公平锁,指的是多个线程在处理任务时,其中一个线程获取到了锁,其他的线程就会按照顺序进入队列进行等待。当锁被释放之后,位于队列的第一个等待线程就会被取出去执行业务。
  • 非公平锁,指的是多个线程对于锁的获取是抢占式的,通过CAS算法随机的进行对锁的抢占,持有。有可能同一个线程会多次抢到同一个锁,所以是不公平的。

2、公平锁
2.1 lock-加锁

下面就以公平锁的源码实现来理解AQS

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

当调用 FairSync.lock的方法时,其实是调用了AQS.acquire(int arg)方法

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  • tryAcquire(arg):这个方法在AQS中只是抛出了一个 new UnsupportedOperationException()的异常。具体的实现是由FairSync实现的。
    所以,我们接着往下看FairSync.tryAcquire(),是否加锁成功。
        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
        	// 获取当前线程
            final Thread current = Thread.currentThread();
            // 获取当前线程的状态,0 代表没有获得锁,非0 代表获得了锁
            int c = getState();
            if (c == 0) {
				hasQueuedPredecessors() 队列中,当前节点的前节点是否存在。
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                   // 成功cas,那么代表当前线程获得该变量的所有权,也就是说成功获得锁
                   
                    setExclusiveOwnerThread(current);
                    // 设置当前线程为 独占性变量所有者线程
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
            	// 判断当前线程的引用是否与独占性所有者线程的引用一致
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                // setState 加锁成功
                return true;
            }
            return false;
        }
    }
  • acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
    addWaiter(Node.EXCLUSIVE) 没有获取到锁,则将当前线程加入至队列。
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

acquireQueued()方法
由于进入阻塞状态的操作会降低执行效率,所以,AQS会尽力避免试图获取独占性变量的线程进入阻塞状态。所以,当线程加入等待队列之后,acquireQueued会执行一个for循环,每次都判断当前节点是否应该获得这个变量(在队首了)。如果不应该获取或在再次尝试获取失败,那么就调用shouldParkAfterFailedAcquire判断是否应该进入阻塞状态。如果当前节点之前的节点已经进入阻塞状态了,那么就可以判定当前节点不可能获取到锁,为了防止CPU不停的执行for循环,消耗CPU资源,调用parkAndCheckInterrupt函数来进入阻塞状态。

shouldParkAfterFailedAcquire。

2.2 unlock-释放锁
    public void unlock() {
        sync.release(1);
    }

调用sync.release(1),其实质还是调用了AQS.release()

    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()的处理逻辑如下

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

将当前资源进行锁释放。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值