AQS架构

发表于: java/j2ee | 作者: darkmi | 日期: 2014/01/22 08:00
字号:

1. LockSupport类

AQS架构使用LockSupport来实现线程的休眠,时间性休眠,唤醒…

2. ‍AQS架构-所有同步器的基本架构

-–– a. 基于‍‍AQS架构的同步器基本上只需要实现四个函数

1. tryAcqurie(int) – 线程会调用这个函数来做能否互斥的获取同步器资源的尝试的逻辑

2. tryRelease(int) – ‍线程会调用这个函数来做能否互斥的释放同步器资源的尝试的逻辑

3. tryAcqurieShared(int) – ‍线程会调用这个函数来做能否共享的获取同步器资源的尝试的逻辑

‍4. tryReleaseShared(int) – ‍ ‍线程会调用这个函数来做能否共享的释放同步器资源的尝试的逻辑

-–– b. 基于AQS架构同步器分为

互斥同步器 例如‍ReentrantLock

共享同步器 例如‍Semaphore

互斥和共享共存的同步器‍ReentrantReadWriteLock

接下来会有文章依次介绍这些同步器的实现

-–– c. AQS数据结构

1. 对于首次进入时获取资源失败的Thread,会将其封装到一个NODE数据块里面

2. 会维护两个NODE引用,分别指向NODE双向链表的头和尾,这样来实现FIFO

3. 对于同步器所拥有的资源就是使用一个INT来表示,这也就是‍互斥和共享共存的

同步器‍同时共享的线程只能是2个BYTE能表示的长度的原因,所有又推出了一个LONG型的AQS架构

-–– d. AQS核心代码

1. acquireQueued() 实现了线程不可中断模式下的互斥获取资源

其主要逻辑是一个死循环

a. 线程所属的NODE->prev是HEAD才会调用‍tryAcqurie执行尝试获取锁的逻辑

b. 如果尝试获取锁成功则head=NODE,以及一个释放HARD REFERENCE的操作

c. 返回线程在WAIT期间是否被标记中断 —> 线程摆脱同步器的控制

d. 对‍‍tryAcqurie尝试失败后是否需要WAIT做出判断 –– 重点处

1. 如果前一个NODE处于WAIT则它一定要WAIT

2. 如果前一个NODE处于CANCEL则做一个链表的删除工作

3. 如果前一个NODE是最后加入的则将其标记为WAIT

4. 2和3会让线程再做一次尝试,不会阻塞线程

e. 如果线程需要阻塞则调用‍LockSupport.park阻塞,两种方式会让线程醒来

1. 线程被标记中断

2. 别的线程‍用‍LockSupport.unpark了它,有线程释放了互斥资源

f. 线程醒来循环到a

g. 程序的逻辑就是一个尝试获取资源,线程阻塞,被唤醒的轮回

2. ‍doAcquireInterruptibly()‍实现了线程可中断模式下的互斥获取资源

其原理和‍acquireQueued()一样,不同之处是线程在被中断后即在1.e.1条件下会直接退出死循环

3. ‍doAcquireNanos‍()‍实现了线程可中断+记时模式下的互斥获取资源

a. 在阻塞前会先计算阻塞的时间,进入休眠

b. 如果被中断则会判断时间是否到了

1. 如果没到则且被其他线程设置了中断标志,退出那个轮回,抛出中断异常,如果没有被设置中断标记则是前一个线程

释放了资源再唤醒了它,其继续走那个轮回,轮回中,如果tryAcquire成功则摆脱了同步器的控制,否则回到a

2. 如果时间到了则退出轮回,获取资源失败

4. release()实现了互斥资源的释放,最主要的工作就是将其下一个线程唤醒‍1.e.2

5. doAcquireShared()‍实现了线程不可中断模式下的共享获取资源

a. 线程所属的NODE->prev是HEAD才会调用‍‍tryAcquireShared执行尝试获取锁的逻辑

b. 如果尝试获取锁成功则head=NODE,以及一个释放HARD REFERENCE的操作

此处和互斥的明显区别是,线程在获取成功后如果还有剩余的资源会唤醒其后的线程

也就是如果其下一个线程也是共享的获取资源,其被唤醒的理由多了个

c. 线程在WAIT期间是否被标记中断则标记,因为这期间的标记状态会被清掉 —> 线程摆脱同步器的控制

d. 对‍‍tryAcquireShared尝试失败后是否需要WAIT做出判断 –– 重点处

1. 如果前一个NODE处于WAIT则它一定要WAIT

2. 如果前一个NODE处于CANCEL则做一个链表的删除工作

3. 如果前一个NODE是最后加入的则将其标记为WAIT

4. 2和3会让线程再做一次尝试,不会阻塞线程

e. 如果线程需要阻塞则调用‍LockSupport.park阻塞,三种方式会让线程醒来

1. 线程被标记中断

2. 别的线程‍释放了资源LockSupport.unpark了它

3. 其前一个共享线程醒了以后获取了资源而其还有资源也会唤醒它

f. 线程醒来循环到a

g. 程序的逻辑就是一个尝试获取资源,线程阻塞,被唤醒的轮回

6. ‍doAcquireSharedInterruptibly()‍实现了线程可中断模式下的共享获取资源

其原理和‍‍doAcquireShared()一样,不同之处是线程在被中断后即在5.e.1条件下会直接退出死循环

7. doAcquireSharedNanos()‍实现了线程可中断+记时模式下的共享获取资源

a. 在阻塞前会先计算阻塞的时间,进入休眠

b. 如果被中断则会判断时间是否到了

1. 如果没到则且被其他线程设置了中断标志,退出那个轮回,抛出中断异常,如果没有被设置中断标记则是前一个线程

释放了资源再唤醒了它,其继续走那个轮回,轮回中,如果tryAcquire成功则摆脱了同步器的控制,否则回到a

2. 如果时间到了则退出轮回,获取资源失败

8. ‍releaseShared()和‍release()一样

-––––––––––––––––––––

[来源:http://www.cnblogs.com/mandela/archive/2011/04/08/2009808.html]

切换主题切换主题
返回顶部