前言
一、构造同步组件的模式丛概念方层面,在中,我们知道锁与同步器的相关概念:
从代码层面,同步器是基于模板模式实现的,可以通过可重写的方法中的随便一个窥探: /** * 模板方法: * protected关键字 * 没有任何实现 * @param arg * @return */protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException(); } 也就是需要进行以下几步: 1)继承同步器重写指定方法(idea中extends AQS点击快捷键ctrl+O即可显示)
2)随后将同步器组合在自定义同步组件的实现中,即定义内部类Syn继承AQS,在Syn中重写AQS方法: public class Sync extends AbstractQueuedSynchronizer{ @Overrideprotected boolean tryAcquire(int arg) {final Thread current = Thread.currentThread();if (compareAndSetState(0, 1)) {// 获取成功之后,当前线程是该锁的持有者,不需要再可重入数 setExclusiveOwnerThread(current);return true; }return false; } @Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) {throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0);return true; } @Overrideprotected boolean isHeldExclusively() { return getState() == 1; }// 返回Condition,每个Condition都包含了一个队列 Condition newCondition() {return new ConditionObject(); } } 3)最后调用同步器提供的模板方法,即同步组件类实现Lock方法之后,在lock/unlock方法中调用内部类Syn的方法acquire(int arg)等方法 public class Mutex implements Lock { ........ @Overridepublic void lock() { sync.acquire(1); } @Overridepublic void unlock() { sync.release(1); } ........ } 具体请看下面的实践部分 二、互斥不可重入锁在我之前写过的博文中(详解Java锁的升级与对比(1)——锁的分类与细节(结合部分源码))介绍可重入锁与不可重入锁的区别时,就写到JUC中没有不可重入锁的具体实现,但是可以类比,现在呢,我们可以做到实现了,具体看下面代码,模式完全符合依赖Lock与AQS构造同步组件模式。 (1)Mutex代码实现(核心关键实现已经在代码中注释)public class Mutex implements Lock {private final Sync sync = new Sync();public class Sync extends AbstractQueuedSynchronizer{ @Overrideprotected boolean tryAcquire(int arg) {final Thread current = Thread.currentThread();if (compareAndSetState(0, 1)) {// 获取成功之后,当前线程是该锁的持有者,不需要再可重入数 setExclusiveOwnerThread(current);return true; }return false; } @Overrideprotected boolean tryRelease(int arg) {if (getState() == 0) {throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0);return true; } @Overrideprotected boolean isHeldExclusively() { return getState() == 1; }// 返回Condition,每个Condition都包含了一个队列 Condition newCondition() {return new ConditionObject(); } } @Overridepublic void lock() { sync.acquire(1); } @Overridepublic void unlock() { sync.release(1); } @Overridepublic void lockInterruptibly() throws InterruptedException { } @Overridepublic boolean tryLock() {return false; } @Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false; } @Overridepublic Condition newCondition() {return null; } } 其中核心代码就是重写的两个方法:
(2)测试Demopublic class MutexDemo { @Testpublic void test(){final Mutex lock = new Mutex();class Worker extends Thread { @Overridepublic void run() {// 一直不停在获取锁while (true) { lock.lock();try { System.out.println(Thread.currentThread().getName() +" hold lock, "+new Date()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() +" release lock, "+new Date()); } } } }for (int i = 0; i < 10; i++) { Worker worker = new Worker();// 以守护进程运行,VM退出不影响运行,这里只是为了一个打印效果,去掉注释一直打印worker.setDaemon(true); worker.start(); }// 每隔一秒换行for (int j = 0; j < 10; j++) {try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); } } } (3)运行结果Thread-0 hold lock, Tue Dec 08 16:26:42 CST 2020Thread-0 release lock, Tue Dec 08 16:26:43 CST 2020Thread-1 hold lock, Tue Dec 08 16:26:43 CST 2020Thread-2 hold lock, Tue Dec 08 16:26:44 CST 2020Thread-1 release lock, Tue Dec 08 16:26:44 CST 2020Thread-3 hold lock, Tue Dec 08 16:26:45 CST 2020Thread-2 release lock, Tue Dec 08 16:26:45 CST 2020Thread-3 release lock, Tue Dec 08 16:26:46 CST 2020Thread-4 hold lock, Tue Dec 08 16:26:46 CST 2020Thread-4 release lock, Tue Dec 08 16:26:47 CST 2020Thread-6 hold lock, Tue Dec 08 16:26:47 CST 2020Thread-7 hold lock, Tue Dec 08 16:26:48 CST 2020Thread-6 release lock, Tue Dec 08 16:26:48 CST 2020Thread-7 release lock, Tue Dec 08 16:26:49 CST 2020Thread-5 hold lock, Tue Dec 08 16:26:49 CST 2020Thread-8 hold lock, Tue Dec 08 16:26:50 CST 2020Thread-5 release lock, Tue Dec 08 16:26:50 CST 2020Thread-8 release lock, Tue Dec 08 16:26:51 CST 2020Thread-9 hold lock, Tue Dec 08 16:26:51 CST 2020 (4)结果分析互斥锁的核心就是同一个同步状态只能被一个线程持有,其它线程等待持有线程释放才能竞争获取。截图一开始的运行结果分析: Thread-0 hold lock, Tue Dec 08 16:26:42 CST 2020Thread-0 release lock, Tue Dec 08 16:26:43 CST 2020Thread-1 hold lock, Tue Dec 08 16:26:43 CST 2020Thread-2 hold lock, Tue Dec 08 16:26:44 CST 2020Thread-1 release lock, Tue Dec 08 16:26:44 CST 2020 10个线程不断竞争锁,一开始Thread-0在08 16:26:42获取到锁,持有锁1秒后在释放16:26:43时释放,同时Thread-1立马获取到锁,1秒后于16:26:44释放锁,同时Thread-2立马获取到了锁...... 根据输出结果来说,完全符合Mutex作为互斥锁这个功能:同一时刻只有一个线程持有锁(同步状态),其它线程等待释放后才能获取。 三、指定共享线程数目的共享锁(1)代码实现(核心关键实现已经在代码中注释)public class MyShareLock implements Lock {// 可以看到共享等待队列中的线程public Collection<Thread> getSharedQueuedThreads(){return syn.getSharedQueuedThreads(); }private final Syn syn = new Syn(2);private static final class Syn extends AbstractQueuedSynchronizer{int newShareCount=0; Syn(int shareCount){if (shareCount <= 0) {throw new IllegalArgumentException("share count must large than zero"); }// 设置初始共享同步状态 setState(shareCount); }/** * 共享锁指定数目 * @param reduceShareCount * @return */@Overrideprotected int tryAcquireShared(int reduceShareCount) {for (;;){int currentShareCount = getState(); newShareCount = currentShareCount- reduceShareCount;if (newShareCount < 0 ||compareAndSetState(currentShareCount,newShareCount)) {// newShareCount大于等于0才说明获取锁成功if (newShareCount >= 0) {// System.out.println(Thread.currentThread().getName()+" hold lock, current share count is "+newShareCount+", "+new Date()); }// newShareCount小于0表示获取失败所以需要返回// compareAndSetState(currentShareCount,newShareCount)为true自然表示成功需要返回return newShareCount; } } } @Overrideprotected boolean tryReleaseShared(int returnShareCount) {for (;;){int currentShareCount = getState(); newShareCount = currentShareCount + returnShareCount;if (compareAndSetState(currentShareCount,newShareCount)) {// System.out.println(Thread.currentThread().getName() +" release lock, current share count is "+newShareCount+", "+new Date());return true; } } }protected int getShareCount(){return getState(); } }/** * 调用内部同步器Syn的acquireShare方法 */@Overridepublic void lock() { syn.acquireShared(1); }/** * 调用内部同步器Syn的releaseShared方法 */@Overridepublic void unlock() { syn.releaseShared(1); } @Overridepublic void lockInterruptibly() throws InterruptedException {if (Thread.interrupted()) {throw new IllegalStateException(); } syn.acquireInterruptibly(1); } @Overridepublic boolean tryLock() {return false; } @Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false; } @Overridepublic Condition newCondition() {return null; } } (2)测试Demopublic class ShareLockTest { @Testpublic void test(){final MyShareLock lock = new MyShareLock();class Worker extends Thread { @Overridepublic void run() {// 一直不停在获取锁while (true) { lock.lock();try { System.out.println(Thread.currentThread().getName() +" hold lock, "+new Date());// System.out.println(lock.getSharedQueuedThreads());Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() +" release lock, "+new Date()); } } } }for (int i = 0; i < 10; i++) { Worker worker = new Worker();// 以守护进程运行,VM退出不影响运行,这里只是为了一个打印效果,去掉注释一直打印worker.setDaemon(true); worker.start(); }// 每隔一秒换行for (int j = 0; j < 10; j++) {try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(); } } } (3)运行结果(结果可能不同)Thread-1 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 release lock, Tue Dec 08 16:36:06 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-1 release lock, Tue Dec 08 16:36:06 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-4 release lock, Tue Dec 08 16:36:07 CST 2020Thread-2 release lock, Tue Dec 08 16:36:07 CST 2020Thread-5 hold lock, Tue Dec 08 16:36:07 CST 2020Thread-8 hold lock, Tue Dec 08 16:36:07 CST 2020Thread-8 release lock, Tue Dec 08 16:36:08 CST 2020Thread-3 hold lock, Tue Dec 08 16:36:08 CST 2020Thread-9 hold lock, Tue Dec 08 16:36:08 CST 2020Thread-5 release lock, Tue Dec 08 16:36:08 CST 2020Thread-6 hold lock, Tue Dec 08 16:36:09 CST 2020Thread-7 hold lock, Tue Dec 08 16:36:09 CST 2020Thread-3 release lock, Tue Dec 08 16:36:09 CST 2020Thread-9 release lock, Tue Dec 08 16:36:09 CST 2020Thread-6 release lock, Tue Dec 08 16:36:10 CST 2020Thread-1 hold lock, Tue Dec 08 16:36:10 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:10 CST 2020Thread-7 release lock, Tue Dec 08 16:36:10 CST 2020Thread-1 release lock, Tue Dec 08 16:36:11 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:11 CST 2020Thread-0 release lock, Tue Dec 08 16:36:11 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:11 CST 2020Thread-2 release lock, Tue Dec 08 16:36:12 CST 2020Thread-8 hold lock, Tue Dec 08 16:36:12 CST 2020Thread-5 hold lock, Tue Dec 08 16:36:12 CST 2020Thread-4 release lock, Tue Dec 08 16:36:12 CST 2020Thread-5 release lock, Tue Dec 08 16:36:13 CST 2020Thread-9 hold lock, Tue Dec 08 16:36:13 CST 2020Thread-3 hold lock, Tue Dec 08 16:36:13 CST 2020Thread-8 release lock, Tue Dec 08 16:36:13 CST 2020Thread-3 release lock, Tue Dec 08 16:36:14 CST 2020Thread-7 hold lock, Tue Dec 08 16:36:14 CST 2020Thread-9 release lock, Tue Dec 08 16:36:14 CST 2020Thread-6 hold lock, Tue Dec 08 16:36:14 CST 2020 (4)结果分析该指定共享线程数量N的共享锁的最终目的就是多个线程可以持有锁(同步状态),达到共享线程数量N(代码中默认为2)时,其它线程将进入Queue等待获取同步结果,同一时刻只能最多有N个线程持有锁。 同样地,我们分析开头运行结果: Thread-1 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 hold lock, Tue Dec 08 16:36:05 CST 2020Thread-0 release lock, Tue Dec 08 16:36:06 CST 2020Thread-4 hold lock, Tue Dec 08 16:36:06 CST 2020Thread-1 release lock, Tue Dec 08 16:36:06 CST 2020Thread-2 hold lock, Tue Dec 08 16:36:06 CST 2020 10个线程不停竞争锁,一开始Thread-0与Thread-1在16:36:05时刻同时获取到了锁,此时已经达到共享数量的最大值,即N,之后持有锁1秒,Thread-0与Thread-1在16:36:06时刻立马释放锁,同时Thread-4与Thread-2立马退出等待队列立马竞争持有锁。 从结果来看,完全是符合ShareLock共享锁功能的:同一时刻最多允许N个线程持有锁,其它线程等待持有线程释放锁! |
|