☕️【Java技术之旅】【AbstractQueuedSynchronizer】教你自定义实现自己的同步器

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"前提概要","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"之前的文章中会涉及到了相关AQS的原理和相关源码的分析,所谓实践是检验真理的唯一标准!接下来就让我们活化一下AQS技术,主要针对于自己动手实现一个AQS同步器。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"定义MyLock实现Lock","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Doug Lea大神在JDK1.5编写了一个Lock接口,里面定义了实现一个锁的基本方法,我们只需编写一个MyLock类实现这个接口就好。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class MyLock implements Lock {\n /**\n * 加锁。如果不成功则进入等待队列\n */\n @Override\n public void lock() {}\n /**\n * 加锁(可被interrupt)\n */\n @Override\n public void lockInterruptibly() throws InterruptedException {}\n /**\n * 尝试加锁\n */\n @Override\n public boolean tryLock() {}\n /**\n * 加锁 带超时的\n */\n @Override\n public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {}\n /**\n * 释放锁\n */\n @Override\n public void unlock() {}\n /**\n * 返回一个条件变量(不在本案例谈论)\n */\n @Override\n public Condition newCondition() {}\n}\n","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","marks":[{"type":"strong","attrs":{}}],"text":"定义好MyLock后,接下来就是实现各个方法的逻辑,达到真正的用于线程间sync互斥的需求。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"自定义一个MySync继承自AQS","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下来我们需要自定义一个继承自AQS的MySync。实现自定义的MySync前,先了解AQS内部的一些基本概念。在AQS中主要的一些成员属性如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/23/23c15489688308fb9c8e865137b725c0.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":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"state:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用于标记资源状态,如果为0表示资源没有被占用,可以加锁成功。如果大于0表示资源已经被占用,然后根据自己的定义去实现是否允许对共享资源进行操作","attrs":{}},{"type":"text","text":"。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如:ReentrantLock的实现方式是当state大于0,那么表示已经有线程获得锁了,我们都知道ReentrantLock是可重入的,其原理就是当有线程次进入同一个lock标记的临界区时。先判断这个线程是否是获得锁的那个线程,如果是,state会+1,此时state会等于2。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当unlock时,会一层一层的减1,直到state等于0则表示完全释放锁成功。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"head、tail:用于存放获得锁失败的线程。在AQS中,每一个线程会被封装成一个Node节点,这些节点如果获得锁资源失败会链在head、tail中,成为一个双向链表结构。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"exclusiveOwnerThread","attrs":{}},{"type":"text","text":":","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"用于存放当前获得锁的线程,正如在state说明的那样。ReentrantLock判断可重入的条件就是用这个exclusiveOwnerThread线程跟申请获得锁的线程做比较,如果是同一个线程,则state+1,并重入加锁成功","attrs":{}},{"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":"知道这些概念后我们就可以自定义一个AQS:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public final class MySync extends AbstractQueuedSynchronizer {\n /**\n * 尝试加锁\n */\n @Override\n protected boolean tryAcquire(int arg) {\n if (compareAndSetState(0, 1)) {\n // 修改state状态成功后设置当前线程为占有锁资源线程\n setExclusiveOwnerThread(Thread.currentThread());\n return true;\n }\n return false;\n }\n /**\n * 释放锁\n */\n @Override\n protected boolean tryRelease(int arg) {\n setExclusiveOwnerThread(null);\n // state有volatile修饰,为了保证解锁后其他的一些变量对其他线程可见,把setExclusiveOwnerThread(null)放到上面 happens-before中定义的 volatile规则\n setState(0);\n return true;\n }\n /**\n * 判断是否是独占锁\n */\n @Override\n protected boolean isHeldExclusively() {\n return getState() == 1;\n }\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"将MySync组合进MyLock","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}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class MyLock implements Lock {\n\n // 组合自定义sync器\n private MySync sync = new MySync();\n\n /**\n * 加锁。如果不成功则进入等待队列\n */\n public void lock() {\n sync.acquire(1);\n }\n /**\n * 加锁(可被interrupt)\n */\n public void lockInterruptibly() throws InterruptedException {\n sync.acquireInterruptibly(1);\n }\n /**\n * 尝试加锁\n */\n public boolean tryLock() {\n return sync.tryAcquire(1);\n }\n /**\n * 加锁 带超时的\n */\n public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {\n return sync.tryAcquireNanos(1, unit.toMillis(time));\n }\n /**\n * 释放锁\n */\n public void unlock() {\n sync.release(0);\n }\n /**\n * 返回一个条件变量(不在本案例谈论)\n */\n @Override\n public Condition newCondition() {\n return null;\n }\n}\n","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":"完成整个MyLock的逻辑后,发现在lock()、unlock()中调用的自定义sync的方法tryAcquire()和tryRelease()方法。我们就以在lock()方法中调用acquire()方法说明模板设计模式在AQS中的应用。","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":"点进.acquire()方法后,发现改该方法是来自AbstractQueuedSynchronizer中:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4a/4aa6d2a4de57abc59c114214485bfbb3.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":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在这里面可以看到tryAcquire方法,继续点进去看看tryAcquire(),发现该方法是一个必须被重写的方法,否则抛出一个运行时异常。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模板方法设计模式在这里得以体现,再回到我们第二部中自定义的MySync中,就是重写了AQS中的tryAcquire()方法。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c66b7c14a948aeaefdbfbc8e8ed1249a.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},"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":"调用MyLock的lock(),lock()方法调用AQS的acquire()方法","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在acquire()方法中调用了tryAcquire()方法进行加锁","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而tryAcquire()方法在AQS中是一个必须让子类自定义重写的方法,否则会抛出一个异常","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此调用tryAcquire()时实际上是调用了我们自定义的MySync类中tryAcquire()方法","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"总结","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AQS作为Java并发体系下的关键类,在各种并发工具中都有它的身影,如ReentrantLock、Semaphore等。这些并发工具用于控制sync互斥的手段都是采用AQS,外加Cas机制。AQS采用了模板方法设计模式让子类们自定义sync互斥的条件,比如本案例中MySync类重写了tryAcquire方法。","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":"下面实现一个自定义的sync:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class SelfSynchronizer {\n\n private final Sync sync = new Sync();\n\n public void lock() {\n sync.acquire(1);\n }\n\n public boolean tryLock() {\n return sync.tryAcquire(1);\n }\n\n public boolean unLock() {\n return sync.release(1);\n }\n\n static class Sync extends AbstractQueuedSynchronizer {\n //是否处于占用状态\n @Override\n protected boolean isHeldExclusively() {\n return getState() == 1;\n }\n\n /**\n * 获取sync资源\n * @param acquires\n * @return\n */\n @Override\n public boolean tryAcquire(int acquires) {\n if(compareAndSetState(0, 1)) {\n setExclusiveOwnerThread(Thread.currentThread());\n return true;\n }\n //这里没有考虑可重入锁\n /*else if (Thread.currentThread() == 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 /**\n * 释放sync资源\n * @param releases\n * @return\n */\n @Override\n protected boolean tryRelease(int releases) {\n int c = getState() - releases;\n boolean free = false;\n if (c == 0) {\n free = true;\n }\n setState(c);\n return free;\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":"ReentrantLock源码和上面自定义的sync很相似,测试下该sync,i++在多线程下执行情况:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class TestSelfSynchronizer {\n private static int a = 0;\n private static int b = 0;\n private static SelfSynchronizer selfSynchronizer = new SelfSynchronizer();\n private static ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 50, 1, TimeUnit.SECONDS,\n new LinkedBlockingQueue());\n private static ExecutorService ec = Executors.newFixedThreadPool(20);\n public static void main(String[] args) throws InterruptedException {\n for (int i = 0; i < 20 ; i++) {\n executor.submit(new Task());\n }\n for (int j = 0; j < 20 ; j++) {\n ec.submit(new TaskSync());\n }\n Thread.sleep(10000);\n System.out.println(\"a的值:\"+ a);\n System.out.println(\"b的值\" + b);\n executor.shutdown();\n ec.shutdown();\n }\n static class Task implements Runnable {\n @Override\n public void run() {\n for(int i=0;i<10000;i++) {\n a++;\n }\n }\n }\n static class TaskSync implements Runnable {\n @Override\n public void run() {\n for (int i = 0; i < 10000; i++) {\n //使用sync器加锁\n selfSynchronizer.lock();\n b++;\n selfSynchronizer.unLock();\n }\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":"开启两个线程池,对int型变量自增10000次,如果不加sync器,最后值小于200000,使用了自定义sync器则最后值正常等于200000,这是因为每次自增操作加锁","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章