Java 基于AQS实现自定义同步器的示例

时间:2021-05-20

一、AQS-条件变量的支持

在如下代码中,当另外一个线程调用条件变量的signal方法的时候(必须先调用锁的lock方法获取锁),在内部会把条件队列里面队头的一个线程节点从条件队列里面移除并且放入AQS的阻塞队列里面,然后激活这个线程。

publicfinalvoidsignal(){if(!isHeldExclusively()){throwIllegalMonitorException();}Nodefirst=firstWaiter;if(first!=null){//将条件队列头元素移动到AQS队列doSignal(first);}}
  • 需要注意的是,AQS提供了ConditionObject的实现,并没有提供newCondition函数,该函数用来new一个ConditionObject对象,需要由AQS的子类来提供newConditon函数
  • 下面来看当一个线程调用条件变量的await()方法而被阻塞后,如何将其放入条件队列
privateNodeaddConditionWaiter(){Nodet=lastWaiter;...//(1)Nodenode=newNode(Thread.currentThread(),Node.CONDITION);//(2)if(t==null){firstWaiter=node;}else{t.nextWaiter=node;//(3)}lastWaiter=node;//(4)returnnode;}
  • 代码(1)首先根据根据当前线程创建了一个类型为Node.CONDITION的节点,然后通过代码(2),(3),(4)在单向队列尾部插入一个元素
  • 注意:当多个线程同时调用lock.lock()方法获取锁时,只有一个线程获取到了锁,其他线程会被转换为Node节点插入到lock锁对应的AQS阻塞里面,并且做自旋CAS尝试获取锁
  • 如果获取到了锁的线程又调用对应条件变量的await()方法,则该线程会释放获取到的锁,并被转化为Node节点插入到条件变量对应的条件队列里面
  • 这时候因为调用lock.lock()方法被阻塞到AQS队列里面的一个线程会获取到被释放的锁,如果该线程也调用了条件变量的await()方法则该线程也会被放入条件变量的条件队列里面
  • 当另外一个线程调用条件变量的signal()或者signalAll()方法的时候,会把条件队列里面的一个或者全部Node节点移动到AQS的阻塞队列里面,等待时机获取锁。
  • 最后使用一个图总结:一个锁对应一个AQS阻塞队列,对应多个条件变量,每个条件变量有自己的一个条件队列。

二、基于AQS实现自定义同步器

  • 基于AQS实现一个不可重入的锁,自定义AQS需要重写一系列的函数,还需要定义原子变量state的含义,在这里我们定义state为0表示目前锁没有被线程持有,state为1表示所已经被某一个线程持有,由于是不可重入锁,所以不需要记录持有锁的线程获取锁的次数,另外,我们自定义的锁支持条件变量。
  • 下面来看一下代码实现
packagecom.ruigege.LockSourceAnalysis6;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.locks.Condition;importjava.util.concurrent.locks.Lock;publicclassNonReentrantLockMEimplementsLock,java.io.Serializable{//内部帮助类privatestaticclassSyncextendsAbstractQueueSynchronizer{//是否锁已经被持有protectedbooleanisHeldExclusively(){returngetState()==1;}//如果state为0,则尝试获取锁publicbooleantryAcquire(intacquires){assertacquires==1;if(compareAndSetState(0,1)){setExclusiveOwnerThread(Thread.currentThread());returntrue;}returnfalse;}//尝试释放锁,设置state为0protectedbooleantryRelease(intrelease){assertreleases==1;if(getState()==0){thrownewIllegalMonitorStateException();}setExclusiveOwnerThread(null);setState(0);returntrue;}//提供条件变量接口ConditionnewConditon(){returnnewConditionObject();}}//创建一个Sync来做具体的工作privatefinalSyncsync=newSync();publicvoidlock(){sync.acquire(1);}publicbooleantryLock(){returnsync.tryAcquire(1);}publicvoidunlock(){sync.release(1);}publicConditionnewCondition(){returnsync.newConditon();}publicbooleanisLocked(){returnsync.isHeldExclusively();}publicvoidlockInterruptibly()throwsInterruptedException{sync.acquireInterruptibly(1);}publicbooleantryLock(longtimeout,TimeUnitunit)throwsInterruptedException{returnsync.tryAcquireNanos(1,unit.toNanos(timeout));}}

如上面的代码,NonReentrantLock定义了一个内部类Sync用来实现具体的锁的操作,Sync则继承了AQS ,由于我们实现的独占模式的锁,所以Sync重写了tryAcquire\tryRelease和isHeldExclusively3个方法,另外Sync提供了newCondition这个方法用来支持条件变量。

三、源码:

所在包:com.ruigege.ConcurrentListSouceCodeAnalysis5

https://github.com/ruigege66/ConcurrentJava

以上就是Java 基于AQS实现自定义同步器的示例的详细内容,更多关于Java 基于AQS实现自定义同步器的资料请关注其它相关文章!

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章