Java多线程(七)之同步器基础:AQS框架深入分析(转)
上图中,LOCK的实现类其实都是构建在AbstractQueuedSynchronizer上,为何图中没有用UML线表示呢,这是每个Lock实现 类都持有自己内部类Sync的实例,而这个Sync就是继承AbstractQueuedSynchronizer(AQS)。为何要实现不同的Sync 呢?这和每种Lock用途相关。另外还有AQS的State机制。下文会举例说明不同同步器内的Sync与state实现。
?
?
二、AQS框架如何构建同步器?0、同步器的基本功能?一个同步器至少需要包含两个功能:
1.???????获取同步状态
如果允许,则获取锁,如果不允许就阻塞线程,直到同步状态允许获取。
2.???????释放同步状态
修改同步状态,并且唤醒等待线程。
根据作者论文,?aqs?同步机制同时考虑了如下需求:
1.???????独占锁和共享锁两种机制。
2.???????线程阻塞后,如果需要取消,需要支持中断。
3.???????线程阻塞后,如果有超时要求,应该支持超时后中断的机制。
?
1、同步状态的获取与释放?
AQS实现了一个同步器的基本结构,下面以独占锁与共享锁分开讨论,来说明AQS怎样实现获取、释放同步状态。
?
1.1、独占模式?独占获取:?tryAcquire?本身不会阻塞线程,如果返回?true?成功就继续,如果返回?false?那么就阻塞线程并加入阻塞队列。
?
??
[java] view plaincopy- //get时待用,只检查当前任务是否完成或者被Cancel,如果未完成并且没有被cancel,那么告诉AQS当前线程需要进入等待队列并且park住??protected?int?tryAcquireShared(int?ignore)?{??
- ?????return?innerIsDone()??1?:?-1;??}??
- ??//判定任务是否完成或者被Cancel??
- boolean?innerIsDone()?{??????return?ranOrCancelled(getState())?&&????runner?==?null;??
- }????
- //get时调用,对于CANCEL与其他异常进行抛错??V?innerGet(long?nanosTimeout)?throws?InterruptedException,?ExecutionException,?TimeoutException?{??
- ????if?(!tryAcquireSharedNanos(0,nanosTimeout))??????????throw?new?TimeoutException();??
- ????if?(getState()?==?CANCELLED)??????????throw?new?CancellationException();??
- ????if?(exception?!=?null)??????????throw?new?ExecutionException(exception);??
- ????return?result;??}??
- ??//任务的执行线程执行完毕调用(set(V?v))??
- void?innerSet(V?v)?{???????for?(;;)?{??
- ????????int?s?=?getState();??????????//如果线程任务已经执行完毕,那么直接返回(多线程执行任务?)??
- ????????if?(s?==?RAN)??????????????return;??
- ????????//如果被CANCEL了,那么释放等待线程,并且会抛错??????????if?(s?==?CANCELLED)?{??
- ????????????releaseShared(0);??????????????return;??
- ?????}??????????//如果成功设定任务状态为已完成,那么设定结果,unpark等待线程(调用get()方法而阻塞的线程),以及后续清理工作(一般由FutrueTask的子类实现)??
- ????????if?(compareAndSetState(s,?RAN))?{??????????????result?=?v;??
- ????????????releaseShared(0);??????????????done();??
- ????????????return;???????}??
- ?}??}??
?
以上4个AQS的使用是比较典型,然而有个问题就 是这些状态存在哪里呢?并且是可以计数的。从以上4个example,我们可以很快得到答案,AQS提供给了子类一个int state属性。并且暴露给子类getState()和setState()两个方法(protected)。这样就为上述状态解决了存储问 题,RetrantLock可以将这个state用于存储当前线程的重进入次数,Semaphore可以用这个state存储许可 数,CountDownLatch则可以存储需要被countDown的次数,而Future则可以存储当前任务的执行状态 (RUNING,RAN,CANCELL)。其他的Synchronizer存储他们的一些状态。
AQS留给实现者的方法主要有5个方法,其中 tryAcquire,tryRelease和isHeldExclusively三个方法为需要独占形式获取的synchronizer实现的,比如线 程独占ReetranLock的Sync,而tryAcquireShared和tryReleasedShared为需要共享形式获取的 synchronizer实现。
ReentrantLock内部Sync类实现的 是tryAcquire,tryRelease, isHeldExclusively三个方法(因为获取锁的公平性问题,tryAcquire由继承该Sync类的内部类FairSync和 NonfairSync实现)Semaphore内部类Sync则实现了tryAcquireShared和tryReleasedShared(与 CountDownLatch相似,因为公平性问题,tryAcquireShared由其内部类FairSync和NonfairSync实现)。 CountDownLatch内部类Sync实现了tryAcquireShared和tryReleasedShared。FutureTask内部类 Sync也实现了tryAcquireShared和tryReleasedShared。
参考内容来源:
【java并发】juc高级锁机制探讨
http://singleant.iteye.com/blog/1418580
Java并发同步器AQS(AbstractQueuedSynchronizer)学习笔记(1)
http://my.oschina.net/zavakid/blog/84882
Java并发同步器AQS(AbstractQueuedSynchronizer)学习笔记(2)
http://my.oschina.net/zavakid/blog/85008
JAVA LOCK代码浅析
http://rdc.taobao.com/team/jm/archives/414
java thread 之AQS
http://www.cnblogs.com/nod0620/archive/2012/07/23/2605504.html