AQS介绍和原理分析(下)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"概述","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文首先用导图总结一下上一篇文档写的内容,然后通过Mutex,ReentrantLock说明如何使用AQS,同时关注公平和非公平这个条件,最后关注一下可中断和条件这两个特性。上一篇的总结:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/df/df91b5b055172a3bf3cb6121de43d2ec.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"Mutex&ReentrantLock&公平/非公平","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在jdk8的current包中并没有找到Doug Lea的Mutex类,不过这个不影响,就当我们自己写的也一样。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"class Mutex implements Lock, java.io.Serializable {\n // 自定义同步器\n private static class Sync extends AbstractQueuedSynchronizer {\n // 判断是否锁定状态\n protected boolean isHeldExclusively() {\n return getState() == 1;\n }\n\n // 尝试获取资源,立即返回。成功则返回true,否则false。\n public boolean tryAcquire(int acquires) {\n assert acquires == 1; // 这里限定只能为1个量\n if (compareAndSetState(0, 1)) {//state为0才设置为1,不可重入!\n setExclusiveOwnerThread(Thread.currentThread());//设置为当前线程独占资源\n return true;\n }\n return false;\n }\n\n // 尝试释放资源,立即返回。成功则为true,否则false。\n protected boolean tryRelease(int releases) {\n assert releases == 1; // 限定为1个量\n if (getState() == 0)//既然来释放,那肯定就是已占有状态了。只是为了保险,多层判断!\n throw new IllegalMonitorStateException();\n setExclusiveOwnerThread(null);\n setState(0);//释放资源,放弃占有状态\n return true;\n }\n }\n\n // 真正同步类的实现都依赖继承于AQS的自定义同步器!\n private final Sync sync = new Sync();\n\n //lockacquire。两者语义一样:获取资源,即便等待,直到成功才返回。\n public void lock() {\n sync.acquire(1);\n }\n\n //tryLocktryAcquire。两者语义一样:尝试获取资源,要求立即返回。成功则为true,失败则为false。\n public boolean tryLock() {\n return sync.tryAcquire(1);\n }\n\n //unlockrelease。两者语义一样:释放资源。\n public void unlock() {\n sync.release(1);\n }\n\n //锁是否占有状态\n public boolean isLocked() {\n return sync.isHeldExclusively();\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"读过AQS代码前面的注释,就会了解这种实现就是注释里面建议的AbstractQueuedSynchronizer使用方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过内部类Sync继承AbstractQueuedSynchronizer,通过实现tryAcquire()/tryRelease()或者ryAcquireShared()/tryReleaseShared()方法实现独占/共享方式获取锁。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定义私有变量sync,提供外部接口lock()/unlock()。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来再来看一下ReentrantLock(主要关注它关于公平和非公平的特性)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"static abstract class Sync extends AbstractQueuedSynchronizer\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Sync又有两个子类:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"final static class NonfairSync extends Sync\nfinal static class FairSync extends Sync \n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"显然是为了支持公平锁和非公平锁而定义,默认情况下为非公平锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c6033f0bd8f625a74b3726438a4b8f2c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非公平锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"static final class NonfairSync extends Sync {\n private static final long serialVersionUID = 7316153563782823691L;\n\n /**\n * Performs lock. Try immediate barge, backing up to normal\n * acquire on failure.\n */\n final void lock() {\n if (compareAndSetState(0, 1))\n setExclusiveOwnerThread(Thread.currentThread());\n else\n acquire(1);\n }\n\n protected final boolean tryAcquire(int acquires) {\n return nonfairTryAcquire(acquires);\n }\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公平锁","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"static final class FairSync extends Sync {\n final void lock() {\n acquire(1);\n }\n protected final boolean tryAcquire(int acquires) {\n final Thread current = Thread.currentThread();\n int c = getState();\n if (c == 0) {\n if (!hasQueuedPredecessors() &&\n compareAndSetState(0, acquires)) {\n setExclusiveOwnerThread(current);\n return true;\n }\n }\n else if (current == getExclusiveOwnerThread()) {\n int nextc = c + acquires;\n if (nextc < 0)\n throw new Error(\"Maximum lock count exceeded\");\n setState(nextc);\n return true;\n }\n return false;\n }\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"共同的Sync","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"abstract static class Sync extends AbstractQueuedSynchronizer {\n \n final boolean nonfairTryAcquire(int acquires) {\n final Thread current = Thread.currentThread();\n int c = getState();\n if (c == 0) {\n if (compareAndSetState(0, acquires)) {\n setExclusiveOwnerThread(current);\n return true;\n }\n }\n else if (current == getExclusiveOwnerThread()) {\n int nextc = c + acquires;\n if (nextc < 0) // overflow\n throw new Error(\"Maximum lock count exceeded\");\n setState(nextc);\n return true;\n }\n return false;\n }\n\n protected final boolean tryRelease(int releases) {\n int c = getState() - releases;\n if (Thread.currentThread() != getExclusiveOwnerThread())\n throw new IllegalMonitorStateException();\n boolean free = false;\n if (c == 0) {\n free = true;\n setExclusiveOwnerThread(null);\n }\n setState(c);\n return free;\n }\n protected final boolean isHeldExclusively() {\n return getExclusiveOwnerThread() == Thread.currentThread();\n }\n final boolean isLocked() {\n return getState() != 0;\n }\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里只贴出了部分代码。小总结:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"这里和Mutex的实现方式相似,都是通过内部的Sync类来实现。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公平锁和非公平锁的区别只是在获取锁的时候(在入","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AQS","attrs":{}},{"type":"text","text":"的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CLH","attrs":{}},{"type":"text","text":"队列之前)代码有点区别,其它的都是一样的。一旦进入了队列,所有线程都是按照队列中先来后到的顺序请求锁。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"非公平锁的代码中总是优先尝试当前是否有线程持有锁,一旦没有任何线程持有锁,那么非公平锁就霸道的尝试将锁“占为己有”。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"ps:小插曲。这个时候如果有人再问Lock和Synchronized的区别,可以这么回答了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AbstractQueuedSynchronizer通过构造一个基于阻塞的CLH队列容纳所有的阻塞线程,而对该队列的操作均通过Lock-Free(CAS)操作,但对已经获得锁的线程而言,ReentrantLock实现了偏向锁的功能。synchronized的底层也是一个基于CAS操作的等待队列,但JVM实现的更精细,把等待队列分为ContentionList和EntryList,目的是为了降低线程的出列速度;当然也实现了偏向锁,从数据结构来说二者设计没有本质区别。但synchronized还实现了自旋锁,并针对不同的系统和硬件体系进行了优化,而Lock则完全依靠系统阻塞挂起等待线程。当然Lock比synchronized更适合在应用层扩展,可以继承AbstractQueuedSynchronizer定义各种实现,比如实现读写锁(ReadWriteLock),公平或不公平锁;同时,Lock对应的Condition也比wait/notify要方便的多、灵活的多。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是后续的问题,只能祝你好运了……(学海无涯啊)","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"可中断","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前提是AQS的acquire是不响应中断的(关于Intereput的知识点,自行百度一下即可,需要知道线程在运行状态下是不响应中断的)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可中断和超时的实现都是在AbstractQueuedSynchronizer,没有交给子类来实现。如果在获取一个通过网络交互实现的锁时,这个锁资源突然进行了销毁,那么使用acquireInterruptibly的获取方式就能够让该时刻尝试获取锁的线程提前返回。而同步器的这个特性被实现Lock接口中的lockInterruptibly方法。根据Lock的语义,在被中断时,lockInterruptibly将会抛出InterruptedException来告知使用者","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"public final void acquireInterruptibly(int arg)\n\n    throws InterruptedException {\n\n    if (Thread.interrupted())\n\n        throw new InterruptedException();\n\n    if (!tryAcquire(arg))\n\n        doAcquireInterruptibly(arg);\n\n}\n\nprivate void doAcquireInterruptibly(int arg)\n\n    throws InterruptedException {\n\n    final Node node = addWaiter(Node.EXCLUSIVE);\n\n    boolean failed = true;\n\n    try {\n\n        for (;;) {\n\n            final Node p = node.predecessor();\n\n            if (p == head && tryAcquire(arg)) {\n\n                setHead(node);\n\n                p.next = null; // help GC\n\n                failed = false;\n\n                return;\n\n            }\n\n            // 检测中断标志位\n\n            if (shouldParkAfterFailedAcquire(p, node) &&\n\n            parkAndCheckInterrupt())\n\n                throw new InterruptedException();\n\n        }\n\n    } finally {\n\n        if (failed)\n\n            cancelAcquire(node);\n\n    }\n\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述逻辑主要包括:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 检测当前线程是否被中断;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"判断当前线程的中断标志位,如果已经被中断了,那么直接抛出异常并将中断标志位设置为false。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 尝试获取状态;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"调用tryAcquire获取状态,如果顺利会获取成功并返回。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. 构造节点并加入sync队列;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"获取状态失败后,将当前线程引用构造为节点并加入到sync队列中。退出队列的方式在没有中断的场景下和acquireQueued类似,当头结点是自己的前驱节点并且能够获取到状态时,即可以运行,当然要将本节点设置为头结点,表示正在运行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4. 中断检测。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在每次被唤醒时,进行中断检测,如果发现当前线程被中断,那么抛出InterruptedException并退出循环。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"# 超时","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"针对超时控制这部分的实现,主要需要计算出睡眠的delta,也就是间隔值。间隔可以表示为nanosTimeout = 原有nanosTimeout – now(当前时间)+ lastTime(睡眠之前记录的时间)。如果nanosTimeout大于0,那么还需要使当前线程睡眠,反之则返回false。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"private boolean doAcquireNanos(int arg, long nanosTimeout)\n\nthrows InterruptedException {\n\n    long lastTime = System.nanoTime();\n\n    final Node node = addWaiter(Node.EXCLUSIVE);\n\n    boolean failed = true;\n\n    try {\n\n        for (;;) {\n\n            final Node p = node.predecessor();\n\n            if (p == head && tryAcquire(arg)) {\n\n                setHead(node);\n\n                p.next = null; // help GC\n\n                failed = false;\n\n                return true;\n\n            }\n\n            if (nanosTimeout <= 0)                 return false;             if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)\n\n            LockSupport.parkNanos(this, nanosTimeout);\n\n            long now = System.nanoTime();\n\n            //计算时间,当前时间减去睡眠之前的时间得到睡眠的时间,然后被\n\n            //原有超时时间减去,得到了还应该睡眠的时间\n\n            nanosTimeout -= now - lastTime;\n\n            lastTime = now;\n\n            if (Thread.interrupted())\n\n                throw new InterruptedException();\n\n        }\n\n    } finally {\n\n        if (failed)\n\n            cancelAcquire(node);\n\n    }\n\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 加入sync队列;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"将当前线程构造成为节点Node加入到sync队列中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 条件满足直接返回;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"退出条件判断,如果前驱节点是头结点并且成功获取到状态,那么设置自己为头结点并退出,返回true,也就是在指定的nanosTimeout之前获取了锁。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. 获取状态失败休眠一段时间;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通过LockSupport.unpark来指定当前线程休眠一段时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4. 计算再次休眠的时间;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"唤醒后的线程,计算仍需要休眠的时间,该时间表示为nanosTimeout = 原有nanosTimeout – now(当前时间)+ lastTime(睡眠之前记录的时间)。其中now – lastTime表示这次睡眠所持续的时间。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5. 休眠时间的判定。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"唤醒后的线程,计算仍需要休眠的时间,并无阻塞的尝试再获取状态,如果失败后查看其nanosTimeout是否大于0,如果小于0,那么返回完全超时,没有获取到锁。 如果nanosTimeout小于等于1000L纳秒,则进入快速的自旋过程。那么快速自旋会造成处理器资源紧张吗?结果是不会,经过测算,开销看起来很小,几乎微乎其微。Doug Lea应该测算了在线程调度器上的切换造成的额外开销,因此在短时1000纳秒内就让当前线程进入快速自旋状态,如果这时再休眠相反会让nanosTimeout的获取时间变得更加不精确。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"![Qz7aVLTPSvEul5y](https://i.loli.net/2021/07/02/Qz7aVLTPSvEul5y.png)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"条件中断","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"参考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.cnblogs.com/onlywujun/articles/3531568.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"http://ifeve.com/introduce-abstractqueuedsynchronizer/","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章