自动刷网站关键字排行,广州购物商城网站,郑州高新区做网站开发的公司,html个人网页制作步骤文章目录 前言synchronized 的加锁过程1.无锁 - 偏向锁2. 偏向锁 - 轻量级锁3. 轻量级锁 - 重量级锁 锁的优化操作1. 锁消除2. 锁粗化 相关面试题 前言
前面我们学习了关于JavaEE多线程方面的锁策略以及 synchronized 分别对应哪些锁策略#xff0c;并且我们还了… 文章目录 前言synchronized 的加锁过程1.无锁 - 偏向锁2. 偏向锁 - 轻量级锁3. 轻量级锁 - 重量级锁 锁的优化操作1. 锁消除2. 锁粗化 相关面试题 前言
前面我们学习了关于JavaEE多线程方面的锁策略以及 synchronized 分别对应哪些锁策略并且我们还了解了关于 CAS 操作在某些情境下不需要加锁而避免因竞争锁造成的阻塞等待状态。今天我将为大家分享 synchronized 的加锁过程以及编译器对加锁过程的一些优化操作。
synchronized 的加锁过程
当我们使用 synchronized 进行枷锁的时候往往不是立即就对该线程进行加锁的而是需要经过一个 无锁 - 偏向锁 - 轻量级锁 - 重量级锁 的过程那么接下来我们来看看这个过程是如何进行的。 synchronized 在 Java 6 之后进行了优化引入了无锁、偏向锁、轻量级锁和重量级锁四种状态这四种状态会随着竞争情况逐渐升级。锁可以升级但不能降级。
1.无锁 - 偏向锁
当我们使用 synchronized 进行加锁的时候并不会立即从 无锁的状态转换为加锁 的状态的而是会先处于一个偏向锁的状态什么叫做偏向锁呢 偏向锁并不是真正的“加锁”而是给对象头中做一个“偏向锁的标记”记录这个锁属于哪个线程。如果后续没有其他线程来竞争该锁,那么就不用进行其他同步操作了(避免了加锁解锁的开销)如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了,很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进入一般的轻量级锁状态。 偏向锁就有点类似于前面我们学习单例模式时候的懒汉模式就是能不加锁的时候就尽量不加锁避免不必要的开销。
2. 偏向锁 - 轻量级锁
当有其他线程开始跟当前线程竞争锁的时候因为该线程已经在对象头中进行了标记那么该线程就可以直接获取到这个锁进入轻量级锁的状态这个轻量级锁也就是自适应的自旋锁而这个自旋锁就是由 CAS操作实现的防止进入内核态操作使线程进入阻塞等待状态。
通过 CAS 检查并更新一块内存 (比如 null 该线程引用)如果更新成功, 则认为加锁成功如果更新失败, 则认为锁被占用, 继续自旋式的等待(并不放弃 CPU)
3. 轻量级锁 - 重量级锁
如果后面发生锁竞争比较激烈的话synchronized 就会从 轻量级锁转换为重量级锁挂起等待锁会使线程进入阻塞等待状态。
执行加锁操作, 先进入内核态.在内核态判定当前锁是否已经被占用如果该锁没有占用, 则加锁成功, 并切换回用户态.如果该锁被占用, 则加锁失败. 此时线程进入锁的等待队列, 挂起. 等待被操作系统唤醒.经历了一系列的沧海桑田, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒 这个线程, 尝试重新获取锁
锁的优化操作
锁消除锁粗化
1. 锁消除
Java 锁消除Lock Elimination是 Java 虚拟机JVM中的一种优化技术用于消除不必要的同步锁操作从而提高程序的性能和并发性。
在 Java 中synchronized 关键字可以用于实现同步和互斥以确保多个线程对共享资源的访问的正确性。然而synchronized 也会引入一定的开销包括获取锁、执行同步代码块、释放锁等操作的时间成本以及可能导致线程阻塞和上下文切换的代价。
为了减少 synchronized 带来的开销JVM 使用了锁消除优化技术。锁消除的基本原理是如果 JVM 检测到某个同步代码块中不存在共享数据的竞争访问即该代码块不会被多个线程同时访问那么 JVM 可以安全地消除该代码块的同步锁操作。
public class Demo2 {private static int num;public static void main(String[] args) {synchronized (Demo2.class) {for(int i 0; i 10; i) {num i;}System.out.println(num);}}
}在这个例子中synchronized 代码块中只有一个成员变量 num并且没有其他线程可以访问到它。因此JVM 可以安全地消除该代码块的同步锁操作从而提高程序的性能。
需要注意的是锁消除只是一种优化技术不能保证在所有情况下都能消除同步锁操作。此外如果程序中存在真正的并发竞争访问那么使用 synchronized 仍然是必要的以确保数据的正确性和一致性。
锁消除是编译器在编译阶段就进行的操作而偏向锁则是在代码的运行过程中实现的。
2. 锁粗化
因为进行加锁和解锁都会造成资源的浪费所以在一些情况下就可以减少加锁和解锁的次数也就是锁粗化的操作。
锁粗化的基本思路是将多个相邻的同步代码块合并为一个更大的同步代码块以减少获取和释放锁的次数。这样可以降低同步操作的开销并减少线程阻塞和上下文切换的可能性。
public class Demo3 {private static int num;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {for(int i 0; i 100; i) {synchronized (Demo2.class) {num;}}});Thread t2 new Thread(() - {for(int i 0; i 100; i) {synchronized (Demo2.class) {num;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(num);}
}上面这个代码两个线程中的任务都是循环了100次但是每次循环都需要进行加锁和解锁的操作这样会浪费大量的资源所以这个代码就可以进行锁粗化的优化操作。
public class Demo3 {private static int num;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {synchronized (Demo2.class) {for(int i 0; i 100; i) {num;}}});Thread t2 new Thread(() - {synchronized (Demo2.class) {for(int i 0; i 100; i) {num;}}});t1.start();t2.start();t1.join();t2.join();System.out.println(num);}
}当进入线程的时候就进行加锁当循环100次结束之后再进行解锁操作这样就避免了资源的浪费。
并不是所有的加锁操作都可以进行锁粗化的优化我们要保证最终代码的执行结果是正确的情况下才能做出相应的优化操作。
相关面试题
1) 什么是偏向锁?
偏向锁不是真的加锁, 而只是在锁的对象头中记录一个标记(记录该锁所属的线程). 如果没有其他线 程参与竞争锁, 那么就不会真正执行加锁操作, 从而降低程序开销. 一旦真的涉及到其他的线程竞 争, 再取消偏向锁状态, 进入轻量级锁状态.
2) synchronized 实现原理 是什么
本博客所写的内容