锁的目的是解决资源争用问题,不同类型的锁是为了在不同场景下资源争用更高效。
当然Java解决资源争用问题,不仅提供了有锁编程,还提供了原子类、分段锁等可以直接使用的原生类,在java.lang.concurrent
包下。
但需要注意的是,无锁编程适用于竞争不激烈的情况下,CAS太多性能同样会下降。
Java 中根据不同的分类标准,可以将锁如下分类,一种锁可能同时具有其他几个特性。
* 偏向锁 / 轻量级锁 / 重量级锁
* 可重入锁 / 非可重入锁
* 共享锁 / 独占锁
* 公平锁 / 非公平锁
* 悲观锁 / 乐观锁
* 自旋锁 / 非自旋锁
* 可中断锁 / 不可中断锁
JVM进程内实现锁的方式可以分为两类
- 内置锁,JVM 内部支持的,sychronized关键字
由JVM控制CAS,对用户透明
- 基于Lock接口的,比如ReentrantLock等,需要手动加锁和释放
基于 AQS(AbstractQueuedSynchronizer),通过 CAS + CLH 队列实现。
无锁升级机制:Lock 的实现始终是“重量级”的(依赖队列阻塞线程),但通过 自旋尝试 优化短期竞争。
synchronized 关键字
理解JVM内置锁,需要理解Java对象头,对象头包括两部分:Mark Word 和 类型指针。32位虚拟机占用4+4共8个字节;64位指针占用8+8共16个字节,默认开启指针压缩是8+4+4(对齐补全)16个字节。
关键理解mark word结构,是一个bitmap,不同bit代表不同的含义。
从图中可以看到不同所状态下,对象头内容的变化。
mark word 结构
阅读这篇文章,写的非常好
https://www.cnblogs.com/hongdada/p/14087177.html
那么轻量级锁和偏向锁的使用场景为:
- 偏向锁则是在只有一个线程执行同步块时进一步提高性能(一次自旋)
- 轻量级锁是为了在线程交替执行同步块时提高性能(多次自旋)
- 重量级锁(线程挂起)
synchronized
有两种用法,分别是对象锁
和类锁
。可以用在方法上,也可以用在代码块上。
内置锁原理