- 视频
- cdsn blog
- synchronized
- 早期sdk就有
- 重量级的锁,会去调用操作系统相关函数
- 后面的升级版本尽量留在jvm而不是操作系统
- 自旋锁
- 单纯自旋: CAS CompareAndSet: 死循环太蠢
- 自旋 + yeild :cpu调度可能还是再调度统一线程
- 自旋 + sleep: sleep多久也不好控制
- 自旋 + park
- 通过CAS state=1 有一个线程能锁上,while死循环结束,搞定
- 其他都
- 外部调用unlock,设置state=0,
- 于是这个线程应该就可以获取到锁了。。。再unlock,再唤醒
- reentrantlock
- 是公平锁还是非公平锁
- 默认是非公平锁
- 重入锁:
- 保存了当前持有锁的线程,如果当前线程匹配,那就给state+1然后return true所以是可以重入的。
- 是公平锁还是非公平锁
- 源码:公平版的reentrantlock
- lock
- acquire(1) :如果尝试获取锁失败 且 获取排队成功,则当前线程中断;如果获取锁成功 或者 获取排队失败 就返回了
- if (!tryAcquire(arg) &&
- 获取当前线程
- 如果state==0,如果不用排队且CAS设置state=acquires成功,就设置当前线程占用了锁,返回成功
- if (!hasQueuedPredecessors() &&
- 头不等于尾,且(头的下一个节点为空或者下一个节点的线程不是当前线程???)
- compareAndSetState(0, acquires)) {
- setExclusiveOwnerThread(current);
- return true;}
- if (!hasQueuedPredecessors() &&
- 如果 state!=0: 如果当前线程是占有锁的线程, 设置state+=acquires,返回成功【可重入】
- 其他情况返回失败
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- 死循环
- 获取当前node的前一个节点
- 如果前一个节点是队头 且 tryAcquire(arg)成功,把队头设置为当前节点,设置interrupted=false并返回
- 否则,如果应该park(shouldParkAfterFailedAcquire)且 parkAndCheckInterrupt, 设置interrupted=true
- shouldParkAfterFailedAcquire(Node pred, Node node)
- 获取前一个节点pred的waitStatus
- 如果ws 是 SIGNAL,返回true
- 如果ws > 0 (CANCEL)
- 循环把前面节点ws>0的都干掉(他们都不排了)
- 否则
- 设置pred的ws为SINGAL
- parkAndCheckInterrupt()
- shouldParkAfterFailedAcquire(Node pred, Node node)
- 有异常的话放弃获取 cancelAcquire(Node node)
- 死循环
- selfInterrupt();
- if (!tryAcquire(arg) &&
- acquire(1) :如果尝试获取锁失败 且 获取排队成功,则当前线程中断;如果获取锁成功 或者 获取排队失败 就返回了
- lock
- 站在AQS的角度看下问题:AQS是啥?AbstractQueuedSynchronizer,抽象的队列同步器。真抽象。其中一个用途就是在reentrantlock里,让不同的线程在这个队列里排队。
- 那我的AQS应该提供什么功能呢?
- 1、排队:addWaiter:通过CAS的方法在队尾添加节点
- 2、叫醒队头的小伙伴:unparkSuccessor:获取链表头,下一个节点才是队头,拿出它里面的线程,unpark它。
- 3、尝试获取锁:acquireQueued(node, arg) 传入一个Node,看他是不是排队头,是的话尝试获取锁,否则park休眠。
- 所以看来reentrantlock大部分就是用的AQS提供的功能了
- 那我的AQS应该提供什么功能呢?
- 你会怎么利用AQS来实现这个锁?
- 首先继承AQS的功能
- lock()
- 设置一个state变量,先试一次CAS state,成功就获得锁(tryAcquire)
- 不行就addWaiter排队,当前线程封装成节点插入到队列里,然后acquireQueued
- 这个得死循环直到获取成功:
- 根据node,获取node前面的节点,看看是不是头结点。是的话说明已经可能马上轮到我了,赶紧tryAcquire一下,成功就ok了,失败的话继续park吧。。。
- 实际上reentrantlock并没有直接park,而是又判断一下shouldParkAfterFailedAcquire,利用waitStatus又一番操作:看下前面节点waitStatus是不是SIGNAL,
- 是singal:才park,
- 不是的话,如果cancel了,需要把前面cancel的节点干掉;
- 其他:=0或者propagate,将其设置成singal。
- 实际上reentrantlock并没有直接park,而是又判断一下shouldParkAfterFailedAcquire,利用waitStatus又一番操作:看下前面节点waitStatus是不是SIGNAL,
- 根据node,获取node前面的节点,看看是不是头结点。是的话说明已经可能马上轮到我了,赶紧tryAcquire一下,成功就ok了,失败的话继续park吧。。。
- 这个得死循环直到获取成功:
- waitStatus有啥用哦?
- 每个node里都有这么一个属性值
- 不同的val
- =1:CANCELLED:由于超时或者中断,node取消了
- =-1:说明后面的小伙伴需要unpark了。【也是醉醉的,用前面的节点来指明后面的节点需要unpark】
- 看一个获取锁的过程:自己写的哦
Java各种lock:原理:直到最底层
最新推荐文章于 2024-07-02 14:03:23 发布