Java各种lock:原理:直到最底层

  • 视频
  • 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;}
          • 如果 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()
          • 有异常的话放弃获取 cancelAcquire(Node node)
        • selfInterrupt();
  • 站在AQS的角度看下问题:AQS是啥?AbstractQueuedSynchronizer,抽象的队列同步器。真抽象。其中一个用途就是在reentrantlock里,让不同的线程在这个队列里排队。
    • 那我的AQS应该提供什么功能呢?
      • 1、排队:addWaiter:通过CAS的方法在队尾添加节点
      • 2、叫醒队头的小伙伴:unparkSuccessor:获取链表头,下一个节点才是队头,拿出它里面的线程,unpark它。
      • 3、尝试获取锁:acquireQueued(node, arg) 传入一个Node,看他是不是排队头,是的话尝试获取锁,否则park休眠。
    • 所以看来reentrantlock大部分就是用的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。
  • waitStatus有啥用哦?
    • 每个node里都有这么一个属性值
    • 不同的val
      • =1:CANCELLED:由于超时或者中断,node取消了
      • =-1:说明后面的小伙伴需要unpark了。【也是醉醉的,用前面的节点来指明后面的节点需要unpark】
  • 看一个获取锁的过程:自己写的哦
  •  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值