1. AQS是如何将请求的线程封装成Node的呢?又是如何将Node连接成队列的呢?
-
既然是封装,那Node中便会持有一个请求Thread对象,并且为了建立Node之间的联系Node中会维护前置与后置节点指针,而AQS中会维护头尾节点指针,此时注意这里维护的是同步队列(在这个队列上的线程都在不断尝试是否可以获取到锁,因为在同步队列上便可以调用AQS的acquireQueue方法,而这个方法使得为获取到锁的线程检查自己是否有资格获取锁,如果没有,则调用LockSupport().park()方法将Node中的线程状态改为WAITING,等待被唤醒或被中断);
强调同步队列是因为,还有多个等待队列(与synchronized中Monitor对象的WaitSet一个意思,不过Monitor对象只有一个,而AQS可以有多个等待队列,视Conditon的数量为定),并且在Node节点中通过Condition指针维护,因为Node是同步队列与等待队列复用的,所以不可避免的产生了一些冗余;
2. 注意:此时最好要将synchronized的monitor机制与这里的AQS机制联系起来看
: 在monitor机制中获得锁的线程如果调用wait()方法,该线程所持有的锁会被释放并将该线程加入等待队列中,而Condition是调用await()方法将该线程放入对应的Condition所持有的等待队列中去(我觉得可以把Condition理解成操作系统中定义的线程唤醒条件),所以有几个Condition就会有几个对应的等待队列;
2. AQS是如何维护共享变量的可访问性呢?
-
在独占锁中,只有在同步队列的首节点的next节点可以尝试获取共享变量,因为在acquireQueue()方法中是这样定义判断条件的
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false; for (;;) {
//获取当前节点的前置节点
final Node p = node.predecessor();
//注意这个与判断条件,第一个就是当前节点的前置节点是否是头节点,而作为第一个判断条件是因为与判断有一个为非即为非,就不会进行第二个条件的判断,这个在以后的编程中也是值得学习的 if (p == head && tryAcquire(arg)) { setHead(node);
p.next = null; // help GC
failed = false; return interrupted;
}
} finally { if (failed)
cancelAcquire(node);
}
}
这样也就保证了获取共享资源的顺序性(即按照插入到队列的时间来定)
-
那AQS只能用来实现独占且公平锁吗?显然不是,AQS又是如何实现非公平锁和共享锁的呢?其实AQS无论用来实现什么锁,这些锁本质的区别就是在于获取共享资源访问权的方式不同,而独占且公平的锁很明显获取访问权的方式是通过FIFO队列的顺序(即请求访问共享资源的顺序),而共享锁也是一样,只是可以获取访问权的线程数多了些;那么非公平锁是如何实现的呢?其实也很简单,就是舍弃队列的FIFO特性,只要持有共享资源的线程释放了锁,所有的在同步队列中的线程都会通过CAS操作去竞争锁;
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。