AQS

# AQS

AQS 是一个抽样同步器,可以通过这个快速实现一些需要同步对象,可以编写各种锁

意义

使用while编写一个简单的自旋锁很容易,但是有的时候,使用while不断的自旋获取锁资源,在别的线程获取到资源的情况下而且使用较长的时间,当前线程还在不断的自旋,cpu有较大的浪费,但是操作线程的状态比较复杂,使用Sleep或者Park以及什么时候唤醒线程都不方便写,因此如果使用AQS,就不再需要写这些线程唤醒阻塞的代码

使用的算法: CHL

相关链接

https://www.cnblogs.com/yuyutianxia/p/4296220.html
https://www.jianshu.com/p/5ad8539e25c3

简单理解

每一个 AQS 都有一个队列结构,如果没有获取到资源加入到队列末尾,队列里面所有的线程(实际上是一个包装线程的一个节点对象)都是阻塞状态,只有当调用release方法的时候才会唤醒,每次唤醒头节点,当然,因为release有可能被并发调用所以其他的节点也有可能被唤醒,对于每一个唤醒的线程,自旋(一次)判断自己是不是头节点,如果是头节点那么会尝试获取锁,获取成功会移除队列

https://www.cnblogs.com/iou123lg/p/9464385.html
https://www.cnblogs.com/barrywxx/p/8678698.html#4175307

疑问 AQS是否可以用来做分布式锁 例如Redis分布式锁

实际上是可以的重写tryAcquire等,但是有几个地方需要注意

  • 因为分布式锁一般都是分布式应用,可能有多个应用或者服务同时去获取锁
  • 假如 服务A获取到锁 服务B没获取到,服务B的线程会被阻塞 因此B服务会死锁(头节点也没获取到锁,会被park服务A获取到锁也不会通知服务B) acquire 一旦头节点没获取到锁会直接死锁
  • 还需要一些可重入的一些判断

解决方法:

  • 使用Redis订阅发布,分布式锁释放的时候发布一个信号 不过有可能服务A挂掉导致死锁
  • 锁的超时机制 不使用 acquire 使用 tryAcquireNanos 超时机制
  • 线程池扫描,定时调用 release 可以实现,配合一些实践轮算法比较好用

具体的与Redis加锁相关可以参考一下这个

https://yq.aliyun.com/articles/639958?spm=a2c4e.11153940.0.0.7009d8008Drp6g

posted @ 2019-08-18 13:43  stdpain  阅读(259)  评论(0编辑  收藏  举报