AbstractQueuedSynchronizer源码分析(ReentrantLock锁的实现)
1.? 前言 Java中好多地方用到AbstractQueuedSynchronizer(PS:简称AQS),比如ReentrantLock、线程池,这部分在面试的时候也经常被问到,今天以ReentrantLock为例,通过源码来加深对AQS的理解 2.??lock 通常,我们的用法是这样的: 那么lock(),unlock()到底做了什么,我们并不知晓,接下来一步一步揭开她的神秘面纱 2.1.? lock 可以看到,ReentrantLock默认是NonfairSync(非公平锁) 2.2.??NonfairSync 可以看到,首先判断同步状态是否是0,如果是0则临界资源没有被占用,并且将状态设为1,当前线程获得这个资源,可以访问;否则,调用acquire(1)方法尝试获取资源的访问控制权。 2.3.? tryAcquire
尝试再获取一次 如果当前同步状态是0,则跟之前相同,将状态置为1,当前线程获得访问权,返回true 如果当前资源的所有者线程就是当前线程,则状态加1,当前线程仍然获得访问权,返回true 如果以上情况都不是(PS:资源被别的线程占用着),则返回false 2.4.??acquireQueued 如果上一步tryAcquire方法返回false,则继续调用acquireQueued方法将线程添加到队列中(PS:链表的尾部) 在添加到链表之前,先封装成Node对象 将当前线程构造成Node对象,节点的模式是排它的,然后将其加到链表的尾部(或是说叫队列),最后将该结点返回 这里有两个特殊的节点:等待队列的头结点 和 等待队列的尾结点 接下来,将刚才构造的节点加到队列中 如果当前节点的前驱节点是头结点(head),并且当前节点再次尝试获取资源(tryAcquire方法),恰好成功了,于是皆大欢喜 如果当前节点的前驱节点既不是head,而且当前节点也没有抢占到资源,则循环直到前驱节点的状态变成SIGNAL,则挂起当前线程 3.? unlock 如果资源的所有者线程不是当前线程的话,则抛出异常 如果当前资源同步状态减1恰好是0,则成功释放,同步状态置为0,资源所有者线程置为null,唤醒head的后继节点 4.? 小结 加锁 1、默认非公平锁(PS:当前线程不是直接加到等待队列中,而是先尝试获取一次,如果不成功则加到队列中,在加的时候还会尝试一次) 2、临街资源有一个同步状态,0表示当前没有线程占用,则可以直接可以获取到资源(加锁成功) 3、如果资源所有者线程就是当前线程,则状态加1,仍然可以获得锁 3、加锁成功以后,将同步状态置为1,资源所有者线程置为当前线程 4、不成功,则封装成Node加入到等待队列(链表)中,此时,还会像前面一样再尝试(抢占)一次 5、抢占不成功,加到等待队列中,线程挂起 解锁 1、资源所有者线程不是当前线程,则抛异常 2、唤醒下一个结点 ? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |