关于java:Java-并发编程AQS-的自旋锁

5次阅读

共计 1444 个字符,预计需要花费 4 分钟才能阅读完成。

互斥锁在 AQS 的互斥锁与共享锁中曾经做了具体介绍,一个锁一次只能由一个线程持有,其它线程则无奈取得,除非已持有锁的线程开释了该锁。这里为什么提互斥锁呢?其实互斥锁和自旋锁都是实现同步的计划,最终实现的成果都是雷同的,但它们对未取得锁的线程的解决形式却是不同的。对于互斥锁,当某个线程占有锁后,另外一个线程将进入阻塞状态。与互斥锁相似,自旋锁保障了公共数据在任意时刻最多只能由一条线程获取应用,不同的是在获取锁失败后自旋锁会采取自旋的解决形式。

自旋锁

自旋锁是一种非阻塞锁,它的外围机制就在自旋两个字,即用自旋操作来代替阻塞操作。某一线程尝试获取某个锁时,如果该锁曾经被另一个线程占用的话,则此线程将一直循环查看该锁是否被开释,而不是让此线程挂起或睡眠。一旦另外一个线程开释该锁后,此线程便能取得该锁。自旋是一种忙期待状态,过程中会始终耗费 CPU 的工夫片。

为什么自旋

互斥锁有一个很大的毛病,即获取锁失败后线程会进入睡眠或阻塞状态,这个过程会波及到用户态到内核态的调度,上下文切换的开销比拟大。如果某个锁的锁定工夫很短,此时如果锁获取失败则让它睡眠或阻塞的话则有点得失相当,因为这种开销可能比自旋的开销更大。总结起来就是互斥锁更适宜持有锁工夫长的状况,而自旋锁更适宜持有锁工夫短的状况。

自旋锁特点

  • 自旋锁的外围机制就是死等,所有想要取得锁的线程都在不停尝试去获取锁,当然这也会引来竞争问题。
  • 与互斥锁一样,自旋锁也只容许一个线程取得锁。
  • 自旋锁能提供中断机制,因为它并不会进入阻塞状态,所以能很好反对中断。
  • 自旋锁实用于锁持有工夫叫短的场景,即锁爱护临界区很小的常见,这个很容易了解,如果持有锁太久,那么将可能导致大量线程都在自旋,节约大量 CPU 资源。
  • 自旋锁无奈保障公平性,不保障先到先取得锁,这样就可能造成线程饥饿。
  • 自旋锁须要保障各个本地缓存数据的一致性,在多处理器机器上,每个线程对应的处理器都对同一个变量进行读写。每次写操作都须要同步每个处理器缓存,这可能会影响性能。

自旋锁例子

上面看一个简略的自旋锁的实现,次要看 lock 和 unlock 两个办法,Unsafe 仅仅是为操作提供了硬件级别的原子 CAS 操作。对于 lock 办法,如果有若干线程竞争,能胜利通过 CAS 操作批改 value 值为 newV 的线程即是胜利获取锁的线程。它将顺利通过,而其它线程则一直在循环检测 value 值是否改回 0,将 value 改为 0 的操作就是获取锁的线程执行完后对该锁进行开释。对于 unlock 办法,用于开释锁,开释后若干线程又持续对该锁竞争。如此一来,没取得锁的线程也不会被挂起或阻塞,而是一直循环查看状态。

AQS 的自旋机制

AQS 框架中不论是互斥锁还是共享锁实现的根底思维都是基于自旋的机制,不过它对自旋锁做了优化,这个前面会持续解说。比方上面两图为 AQS 框架获取独占锁和共享锁的逻辑,具体的逻辑咱们先不必管,次要关注方框框住的 for(;;) 这行代码。这便是自旋操作,通过有限循环来实现自旋

Java 并发编程

  • Java 并发编程:Java 序列化的工作机制
  • Java 并发编程:并发中死锁的造成条件及解决
  • Java 并发编程:过程、线程、并行与并发
  • Java 并发编程:工作执行器 Executor 接口
  • Java 并发编程:AQS 的互斥锁与共享锁
  • Java 并发编程:什么是 JDK 内置并发框架 AQS
  • Java 并发编程:AQS 的原子性如何保障
  • Java 并发编程:如何避免在线程阻塞与唤醒时死锁
  • Java 并发编程:多线程如何实现阻塞与唤醒
  • Java 并发编程:AQS 的自旋锁

正文完
 0