diff --git a/blogs/Java/口水话/并发相关口水话.md b/blogs/Java/口水话/并发相关口水话.md index 5c197ea..8304881 100644 --- a/blogs/Java/口水话/并发相关口水话.md +++ b/blogs/Java/口水话/并发相关口水话.md @@ -74,7 +74,7 @@ volatile 是如何保证有序性的呢? 对于锁代码块,其实就在代码块的前后增加一对 monitorenter 和 monitorexit 指令。 -在 Java 1.6 时,synchronized 做了大量优化,引入了轻量级锁和偏向锁。此时锁有四种状态,分别是无锁、偏向锁、轻量级锁和重量级锁。这几个状态会随着竞争情况逐渐升级,锁可以升级但不能降级。 +在 Java 1.6 时,synchronized 做了大量优化,引入了轻量级锁和偏向锁。此时锁有四种状态,分别是无锁、偏向锁、轻量级锁和重量级锁。这几个状态会随着竞争情况逐渐升级,锁可以升级但不能降级,不过锁降级确实会发生,只不过概率很小,当 JVM 进入安全点的时候,会检查是否有闲置的 Monitor,然后试图进行降级。 在讲这四种状态之前,首先要先讲一下对象头。 @@ -82,7 +82,7 @@ synchronized 用的锁的信息是存放在 Java 对象头的 Mard Word 标记 下面就先讲一下偏向锁,为什么要有偏向锁呢?其实呢,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。当一个线程访问同步块并获取锁时,会在对象头里记录锁偏向的线程 ID,下次该线程再次进入只需要判断线程 ID 就可以了。 -轻量级锁就是在获取锁的时候,如果获取不到就自旋一段时间再次获取,也就是自旋锁,如果在指定次数没有成功,就会膨胀为重量级锁,当前线程阻塞掉。默认次数好像是 15,当然,后面出了自适应自旋锁,会根据上次自旋的次数来设置。因为长时间的自旋会消耗 CPU,所以会有限制次数这一说。 +接着讲轻量级锁。当有一个线程竞争获取锁时,由于该锁已经是偏向锁,当发现对象头 Mark Word 中的线程 ID 不是自己的线程 ID,就会进行 CAS 操作获取该锁。如果获取到了就替换线程 ID,继续保持偏向锁状态,如果获取不到就自旋一段时间再次获取,也就是自旋锁,如果在指定次数没有成功,就会膨胀为重量级锁,当前线程阻塞掉。默认次数好像是 15,当然,后面出了自适应自旋锁,会根据上次自旋的次数来设置。因为长时间的自旋会消耗 CPU,所以会有限制次数这一说。轻量级锁适用于线程交替执行同步块的场景,绝大部分的锁在整个同步周期都不存在长时间的竞争。 #### Lock 的实现原理 diff --git a/images/JVM/锁升级.png b/images/JVM/锁升级.png new file mode 100644 index 0000000..231877e Binary files /dev/null and b/images/JVM/锁升级.png differ