锁
乐观锁 & 悲观锁
悲观锁:线程在获取资源前,一定要显示的对资源加锁,适用完成后再释放。比如Java 的 synchronized
乐观锁:获取时不加锁,如果需要更新资源状态,再判断资源是否被修改过
乐观锁适合读操作较多的场景,较少的加锁开销(同悲观锁比较)能提升读取性能
自旋锁
线程切换时,操作系统需要在用户态和内核态之间切换,如果同步的代码比较简单,那么让线程继续占用CPU,比阻塞后切换的消耗通常会更少。这种空转的锁,就是自旋锁。
公平锁 & 非公平锁
公平锁:按申请顺序获取锁;吞吐率不如非公平锁,但保证每个线程最终都能获得锁
非公平锁:申请锁时如果获取成功,就获得锁;减少线程切换次数,吞吐率高,可能有线程“饿死”
可重入锁 & 非可重入锁
可重入锁 是指 一个线程持有一个锁时,在锁释放之前,允许这个线程能够多次进入这个锁管理的临界区内
独享锁 & 共享锁
仅允许一个线程持有锁 & 多个线程同时持有一个锁,读写锁是典型的 独享锁 & 共享锁
偏向锁 & 轻量级锁 & 重量级锁
这是在JVM中实现的三种不同级别的锁。
偏向锁:为了在无竞争情况下消除同步操作的消耗。这种锁会偏向第一个申请资源的线程,只要没有其他线程申请该资源,偏向锁将永远不需要释放。可以提高带有同步但无竞争的程序的性能。
轻量级锁:“轻量”是相对于使用操作系统互斥量实现的重量级锁而言的。它的目的是在没有多线程竞争的情况下,减少重量级锁产生的消耗。
重量级锁:使用操作系统互斥量实现,消耗较多。
基于一些研究,人们发现 “synchronization” 常常用于一些并不需要重量级锁的场景,如 实际上并没有资源竞争的情况,以及 实际更适于使用自旋锁的情况。基于此,Java 1.6 引入了偏向锁(Biased Locking)和 轻量级锁(Lightweight Locking)。
线程在竞争资源的过程中,锁的类型会按照 偏向锁 -> 轻量级锁-> 重量级锁 这样的顺序升级,但并不存在降级。那么锁是如何升级的呢?我们首先了解一下Java实现这三种锁的原理。
HotSpot虚拟机中,在对象头中使用一个字长存储关于锁的信息,称为“Mark Word”
图1 mark word 的分布Mark Word 末尾三位 = “001” 时,表示对象“未加锁” 的状态,但禁用了偏向锁。
Mark Word 末尾三位 = “101” 时,表示对象使用偏向锁,如果前几位不为0,则对象已被锁定,且持有锁的线程id保存在前几位。
Mark Word 末尾两位 = “00” 时,表示对象处于被轻量级锁锁定的状态,前面空间存储了指向线程栈帧中锁记录(Lock Record)的指针。
Mark Word 末尾两位 = “10” 时,表示对象处于被重量级锁锁定的状态,前面的空间存储了指向了重量级锁的指针。
Lock Record 是虚拟机在线程栈帧中分配的空间,用于存储持有对象锁的Mark Word的拷贝。
图2 Lock Record偏向锁升级为轻量级锁:
图3 偏向锁->轻量级锁轻量级锁升级为重量级锁:
图3 偏向锁->轻量级锁参考:
《深入理解Java虚拟机:jvm高级特性&最佳实践》
http://www.cnblogs.com/dennyzhangdd/p/6734638.html
http://www.cnblogs.com/paddix/p/5405678.html
https://www.jianshu.com/p/1ea87c152413
https://tech.meituan.com/2018/11/15/java-lock.html
https://www.oracle.com/technetwork/java/javase/tech/biasedlocking-oopsla2006-preso-150106.pdf
https://www.oracle.com/technetwork/java/biasedlocking-oopsla2006-wp-149958.pdf