AbstractQueuedSynchronizer 学习总结

先转一篇博客,写得很好,但也很啰嗦。

http://ifeve.com/introduce-abstractqueuedsynchronizer/

看下AbstractQueuedSynchronizer 的代码,做个精简的总结:

AbstractQueuedSynchronizer 类 维护了以下状态,有2个内部类(static Node,public ConditionObject):

  1. volitile int state.   这是同步的状态变量,竞争资源的时候,通过CAS操作,持有线程释放的时候,直接操作既可。

  2. volitile Node head, tail.  这里维护了一个等待队列,竞争资源失败的,都进入这里排队。

我来总结一下把:

  1. 以ReentrantLock的非公平锁为例,lock流程如下:  

    通过CAS获取锁,如果成功则设置exclusive线程为当前线程。

    如果CAS失败,调用acquired(1),首先调用tryAcquire方法做一个获取尝试,如果又失败了。

    新建一个含有当前线程的Node,并将自己加入等待队列。(如果是第一个的话,就会新建一个空的head Node,并将自己设置为head->next, 并将自己设置为队尾;如果不是第一个,将自己设置为队尾既可。以上2种实现,均是非阻塞算法。对应方法名为:addWaiter以及enq). 

    然后就是调用accquireQueued做一个自旋,然后不断尝试tryAcquire方法获取,多次之后当前线程会被park阻塞。

    tryAcquire方法在继承AbstractQueuedSynchronizer 的类中实现,并且作为Lock或其他并发库工具类的成员变量,这是JDK并发库的通用实现规范。 在公平锁的版本中,检查竞争资源的线程,是否是队首,从而实现了公平性,牺牲了性能。

    如果持有锁的线程释放了锁,那么线程会从park中返回,并且再次做自旋尝试,有可能获取到锁,也有可能被刚刚参与竞争的线程抢到,然后再次被park,等待下次唤醒。

  2. unlock流程如下:

    调用Sync的release方法,release方法调用实现类的tryRelease方法,

    tryRelease方法将state的值减去1或则某个数字,返回值Boolean类型,如果state的数字为0,返回true,表示被占用的资源,已经释放干净。

    如果已经释放干净,且等待队列中还有其他线程,则调用unparkSuccessor方法,将head->next节点的线程unpark,如果head->next的waitStatus标记为取消或则不符合释放条件。则从队尾向前遍历,找到队列中靠前位的第一个需要释放的资源。

  3. 因为state的volitile属性,释放时都写了state的值,所以,每次释放都保证了临界区的操作,都从工作内存同步到了主存,从而实现了Lock的逻辑。 获取锁的时候,读了state(其实还用CAS写了state),保证了之后的所有内存读取都直接来自主存。 以上两条,严密的保证了ReentrantLock锁的特性。

ReentrantReadWriteLock读写锁的实现有些不同,大致如下:

写锁操作state的低16位,每次加锁+1;读锁则写state的高16位,每次加锁 + 65536。高16位表示读锁,低表示写锁(排他锁)。获取读锁的时候要确认无写锁存在,获取写锁的时候要确认无任何锁存在,公平性等其他实现与ReentrantLock类似。


再讲:ConditionObject,他是AQS的内部的实例class,所以可以访问AQS的所有实例以及静态的方法和变量。并且它维护了2个Node: firstWaiter, lastWaiter。

Lock.newCondition()方法返回一个condition对象,每个condition对象都含有一个条件队列。

大致机制如下:

1. await 调用addConditionWaiter方法,将当前线程加入等待队列,然后会休眠当前线程。single方法调用之后,isOnSyncQueue方法将检查当前线程是否在Lock的等待队列上,如果是就进入正常的竞争Lock的state的过程。

2. single方法调用doSignal,会将第一个等待的node从ConditionObject的中移除,并且将其加入Lock的等待队列中。

singleAll会将ConditionObject等待队列中的所有Node都移除,并却加入到Lock的等待队列中。

其中,transferForSignal方法,返回true或则等待队列中无可以唤醒的线程,则doSignal的while循环返回。

3.await的方法,使用带有时间的park方法阻塞线程,超出时间之后,将node加入Lock的等待队列。


以上所有的Lock以及Condition 机制看似简单而清晰,其实代码是有点复杂的。因为,等待队列的实现,都是以双向链表的方式,其中还有必须要的关于waitStatus的检查,对cancel的node剔除等等行为。

Lock的tryLock以及tryLock的带时间版本返回为Boolean,标志锁有没有获取成功。Condition的await的带时间的版本,返回long类型,另一个await到dealline的,返回boolean,标识对条件的等待有没有超时。

它们的实现都很相似,就是park阻塞线程的时候,会设置时间参数,无限循环中检查时间,如果超过,就break或则return。

tryLock到时间直接cancel自己的Node,返回false。 ConditionObject直接将自己加入Lock的等待队列,结束对condition的等待。





你可能感兴趣的:(AbstractQueuedSynchronizer 学习总结)