锁就是改变state的状态,设置当前线程。不行的话就进入到等待队列(双向链表)里面去。
非公平锁的实现原理:
我们先找到ReentrantLock的构造器,默认是非公平锁。
非公平锁的加锁流程。
改变state的值。
原理图:
state head tail exclusiveOwnerThread都是Sync的属性。
state是1,则锁已经被占用了。进入下面的逻辑。
---238---
当出现竞争的时候则:
再尝试一次,这个方法没有讲解。
不成功进入acquireQueued方法,这个方法是尝试创建一个node对象,
addWaiter方法,加到等待队列里面,此时线程还是活着的。
再做最后的挣扎看看是不是能获得锁。node就是thread1的node。
这个代码的意思是:
在你尝试获取锁失败的时候是不是应该阻塞住呢?
第一次执行addWaiter,此时都是活着的,不是-1,没有后续节点的话waitStatus就是0。
进去:
进入tryAcquire,是当前的线程 就是重入锁就+1。
看下addWaiter方法,加锁失败进入队列。相当于EntryList,就是阻塞队列。
失败了进入enq方法排队。
指导成功为止。
---
创建节点之后,进入acquireQueue逻辑。
注意凡是带try的都是只改变状态的。
不是退不出循环而是失败后获得park阻塞。
这个重点。
acquireQueue是非常重要的语法。
死循环,先拿到当前的节点的前驱节点,看是不是头节点,是头结点还是有机会的。
失败。
等待队列是一个双向链表。
p是头节点,说明没有人和他竞争,就再获取一次。
失败了。首先应该park住。
看下几个变量的值:
waitStatus
shoudParkAfterFailedAcquired这个方法就是修改其前驱节点的waitStatus为-1。表示有责任唤醒其后继节点。每次添加都是把这个设置为-1。
第一次返回为false,因为只是将前驱的waitStatus变为-1。
第二次再进入for循环返回true。
再来:
此时假设Thread-0已经加锁了。
阻塞住了,但是不退出循环。
唤醒的话只有上一个节点是有资格唤醒的。
---239---
看下解锁。
进入:
看下tryRelease,只是不设置当前线程持有锁就抛出异常。
则进入unparkSuccessorl流程,唤醒一个阻塞队列的线程,进入这个方法:
唤醒的是离head最近的那个线程。
传进来的是头节点。
之前是一直在这里阻塞的,唤醒则在这里继续往下运行了,就是又进入了循环,是在哪里恢复的呢。
这次进去tryAcquire就成功了。
成功了设置为头节点,原来的就是头就断开了。
这个体现的:
---
这个是哪里的?
加锁的源码赏析:
---240---
此时别忘了我们是非公平的锁。
---241---
可重入的原理。
获取锁:
join wait sleep会抛出异常的。
解读这段代码:
正常获取。
重入就是增加计数器。
--------------------------------------------
释放锁。不是0每次-1,是0,释放唤醒。
---242---
可打断的原理。
关于park这个文章很好:https://www.cnblogs.com/septemberFrost/p/12200755.html
https://blog.csdn.net/fjse51/article/details/53928272
我们知道线程如果一直没办法获取锁的话就会进入acquireQueued方法不断的进行尝试的。
interrupt可以打断park,设置t_counter为1,此时有park就会消耗t_counter,否则返回。
设置中断标记为true就不会park。
_counter为1就不会park。
Thread.interrupted()会清除打断标记不会改变t_counter。
sleep检测到中断会清除打断标志不会改变t_counter。
如果一直尝试不到的话就进入parkAndcheckedInterrupt方法。
被打断了就返回真。
此时其他的线程找到这个线程可以打断也就是终止这个park,向下运行。
不清除打断标记park就会失效的。这个方法返回真。
这个就是清除打断标记下次再park时候还可以park住。
---
打断就是中止park的,现在中国特色线程正在阻塞这,其他的线程来打断了。
走到红箭头,此时会清除打断标记,如果其他线程来打断的话,还可以park住。返回值为真。
再往下进入,打断标记置为true。
但是只是置为true,还是会进入到进入循环,下次还是可以park住的。
------------------------------------------------------------------------------------------------------------------------------------
什么时候会用到呢?之后获得锁之久才会用到。
只有获得到锁才能获得锁作为返回标记返回。返回到哪里了呢?
这里打断标记为真就执行if块里面的内容。
返回到这里。
---
有什么不一样呢?
唤醒就抛出异常。
---243---
非公平锁。
公平锁:
体现公平。不成立才竞争的。
说明当前线程没有资格的。
代码赏析:
---244---