Posted on 2012-04-15 16:27
zljpp 阅读(234)
评论(0) 编辑 收藏
前面已经可以了解到AQS的设计,接下看一下具体的使用和实现:
1、java.util.concurrent 的结构:
曾经有一张图可以十分清楚地展示java.util.concurrent的结构,我也借来用一下,有助于理清楚整个大的结构,接下去主要是对各个部分的具体实现进行分析,首先还是从锁说起;
2、lock部分实现的类图:
上图是整个AQS的类图,红色框类表示JDK文档里面自带的一个简单使用ASQ的互斥锁的实现;接下去我们主要从AQS本身和这个简单的实现来解读源码;当然这里完全可以换为ReentrantLock等我们常使用的锁;只不过他们们本身有更加复杂的策略,但是作为AQS的使用者来说,完全是一样的;
2.1、父类AbstractOwnableSynchronizer:从类图中可以发现这个抽象类只有一个Thread类型的成员变量exclusiveOwnerThread;用来存放独占模式时当前的线程;
2.2、Node类:这个类定义了一系列的静态的常量,类图的中的注释可以看到主要的一部分,如CANCELLED、SIGNAL等,这4个int常量加上0用来作为waitStatus可能的值,用来表示当前线程的状态;另外还有两个Node类型的常量SHARED、EXCLUSIVE,SHARED赋值为了一个空的Node对象,EXCLUSIVE=null;这个两个常量用来作为nextWaiter对象可能的取值,表示下一个在条件队列上的node,因为条件队列只有在互斥模式下有意义,所以如果是共享模式等于一个常量,即是SHARED,而互斥模式下新增第一个节点的时候,由于还没有其他线程在该条件上阻塞,所以初始化为两一个常量EXCLUSIVE,也就是null;
另外剩下就是一个指向对应线程的Thread的对象,和指向前一个和后一个节点的Node类型的对象;
Node类有两个重要的方法,一个是isShared(),很明显是判断当前线程的同步模式,根据前面对nextWaiter变量的说明,很容易想到实现只有一行代码:
final boolean isShared() {
return nextWaiter == SHARED;
}
两外一个是predecessor(),返回当前节点的前一个节点,实现也很简单:返回prev的值,只是增加为null的判断;
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
你一定会很奇怪:为什么要单独提供predecessor,而没有提供对应的获取next的方法呢?我想主要有两个原因:1、在论文里面也提到,next的路径只是作为一个优化路径。如果一个节点的next判断是null,是不能相信的还必须通过从尾部通过prev域反向查找(因为不能实现双向链表的无锁的原子插入)。2、在后面的使用场景中可以发现,其实是把跑出异常作为了结束循环的条件,统一到这里来可以使使用的代码简单整洁。
整个Node类是static、final的,并且是AQS的内部类;
2.3、AQS类,这个类是整个lock框架的核心,代码量也比较大,将在下一节详细的介绍;
未完待续。。。