1、最主要的三个类Sync、NonfairSync、FairSync。 2、Sync继承了AbstractQueuedSynchronizer,AbstractQueuedSynchronizer继承了AbstractOwnableSynchronizer。 NonfairSync、FairSync都继承了Sync。 3、Sync的两个最重要的属性为 1)volatile int state 同步状态:是持有锁的线程数 2)thread exclusiveOwnerThread:是同步器所持有的线程,即当前持有锁的线程 4、AbstractQueuedSynchronizer维护了一个FIFO队列(双向链表结构),用于存放阻塞线程 ConditionObject 也维护了一个FIFO队列(双向链表),用于存放状态(waitStatus)为Node.CONDITION的线程节点。 5、AbstractQueuedSynchronizer实际为CLH锁,即 自旋(无限for循环实现)+CAS(unsafe实现) 6、加锁的大致逻辑(公平锁与非公平锁略有不同):两步 加同步状态+入队 (1)首先线程去获取同步状态(Sync的state属性)。若成功获取同步状态,若为0,则设置同步状态为1(此处,非公平锁会抢占设置同步状态, 而公平锁倾向于让等待时间更长的线程设置同步状态),并将持有锁的线程设置为当前线程; 若不为0,则判断是否为当前线程(可重入的实现),若是则将同步状态数加1。 (2)若获取同步状态失败,则将当前线程封装为一个节点,放入AbstractQueuedSynchronizer的队列尾部。根据节点在尾部的位置不同,做不同的处理。 如果节点的前置节点为Head,那个会重新尝试获取同步状态,如果获取成功,则将该节点设置为Head;如果失败,再判断其前置节点的状态(waitStatus),如果前置节点 的状态(waitStatus)为Node.SIGNAL,则需将当前线程阻塞,若前置节点的状态为Node.CANCELLED,则一次将前面状态为Node.CANCELLED的节点移除。 如果当前线程需要阻塞,则调用LockSupport的park方法(实际上是Unsafe的park方法),如下 public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } 如果当前线程不需要阻塞,则再次重试获取锁。 7、解锁的大致逻辑:两步 减同步状态+唤醒下一个节点(线程) (1)首先是对同步状态的操作(state),每次对state减去指定数值(通常为1,即减去一个线程)。直至state的值为0,将Sync所持有的线程置为null。 (2)如果同步状态释放成功,则唤醒AbstractQueuedSynchronizer队列中的Head节点(事实上,Head节点为虚节点,实际唤醒的是头节点的下一个节点)。 (3)唤醒头节点的主要逻辑如下: 如果头节点的waitStatus<0,即为阻塞状态,需要将waitStatus设置为0.然后获取头节点的下一个节点。若下一个节点为的waitStatus>0(即取消状态), 则继续寻找直至找到状态waitStaus<=0的节点,然后进行唤醒操作(LockSupport.unpark())。
ReentrantLock的要点总结
最新推荐文章于 2024-04-19 10:45:48 发布