synchronized真的不重

简介: synchronized 是java中常见的保证多线程访问共享资源时的安全的一个关键字。很多人在讲到synchronized 时都说synchronized 是一把重量级的锁,那么synchronized 真的很重么?

大家好,我是三友~~

synchronized 是java中常见的保证多线程访问共享资源时的安全的一个关键字。很多人在讲到synchronized 时都说synchronized 是一把重量级的锁,那么synchronized 真的很重么?

synchronized 在jdk 1.6以前(不包括1.6)的确是一把很重的锁,每次使用锁的时候都是直接向操作系统请求的,所以效率低,且占资源,但是在jdk1.6以后,jvm对synchronized 进行了优化,加入了锁升级的功能,使得synchronized 在一些情况下并不是一把重量级的锁,而是一个很轻的一把锁。

本文就来探讨一下,jdk对synchronized 优化,包括锁升级、锁粗化、锁消除。

公众号:三友的java日记

锁升级

锁升级其实是指,随着多线程并发加锁的程度提高而相应的对锁的状态的升级,可以分为:偏向锁、轻量级锁、自旋锁、重量级锁;

偏向锁

偏向锁不是一把锁,而是代表了当前synchronized 锁状态。偏向锁是一把很轻的锁,当只有一个来线程加锁的时候,此时synchronized 锁就会变成偏向锁,偏向锁代表这个锁偏向这个线程,就是说当这个线程再次来加锁的时候,不需要再向操作系统申请资源,而是很快就能获取到锁,减少了申请锁的开销。

为什么需要偏向锁。因为在大多数情况下,多线程竞争同一把锁的情况是很少的,那么在没有多线程并发竞争的情况下,其实没必要再去向系统申请重量级锁了,我就用目前这把偏向锁就够了,因为申请重量级会耗费比较大的资源。

轻量级锁

当随着更多线程来加锁的时候,偏向锁就会无法满足使用的条件了,因为偏向锁认为加锁的线程只有一个。

那么多线程加锁有可能会出现这种情况。当会有两个及以上的线程来加锁,但是没有出现同时来竞争锁的情况,也就是说虽然有多个线程来加锁,可能会出现A线程加完锁之后释放了锁,此时B来加锁,发现并没有线程持有锁,也就说没有线程跟B来竞争,也就相当于多线程来交替加锁的情况。

当出现这种情况的时候,偏向锁就会升级为轻量级锁。轻量级锁就是指虽然可能会出现多线程来加锁的情况,但是并不存在锁竞争的情况,并不会存在锁冲突。

自旋锁

上面说到随着加锁的线程变多,出现了多线程交替加锁的情况,偏向锁会升级为轻量级锁,但是随着并发加锁的线程越来越多,出现了多个线程同时来加锁的情况,也就不是交替加锁,那么此时轻量级锁已经不适合使用了,但是jvm为了防止锁直接升级为重量级锁(因为挂起线程和恢复线程的操作都需要转入到内核态中完成,这些操作给系统的并发性能带来很大的压力),加入了线程自旋的机制。所谓的自旋就是虽然加锁失败了,但是有可能出现其他线程很快就释放锁的情况,那么就尝试不断的自旋来尝试加锁,而不是直接将锁升级为重量级锁。

自旋锁的好处就是用来减少操作系统的压力。

重量级锁

但是随着加锁的线程不断增多,自旋了一定的时间或者次数也没有成功加锁,那么锁就会升级为重量级锁,因为自旋会消耗cpu,一直这么自旋也不是很好的选择,所以就会升级为重量级锁。升级为重量级锁之后,所有来加锁的线程加锁失败之后,就会加入等待的队列中,等待别的线程释放锁之后再进行锁的竞争。

通过以上的分析,我们也看出了,synchronized 在最开始的时候并不是上来就是一把重量级的锁,而是随着多线程并发竞争锁的激烈程度的提高来不断的升级,慢慢变成重量级锁,在整个升级的过程会经历偏向锁、轻量级锁、自旋锁、重量级锁的过程。

锁消除

先来看一段代码

public class SynchronizedDemo {
   

    public static void main(String[] args) {
   
        Object monitor = new Object();
        synchronized (monitor){
   
            System.out.println("加锁成功....");
        }
    }
}

通过上面代码我们可以看出,synchronized 的锁对象其实是方法的一个局部变量,那么对于这种情况,不会出现多线程竞争同一把Object 对象锁的情况,那么在运行的时候就会忽略这个synchronized 加锁的过程。

说的官方一点就是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

锁粗化

public class SynchronizedDemo {
   

    private static final Object MONITOR = new Object();

    public static void main(String[] args) {
   

        for (int i = 0; i < 10; i++) {
   
            synchronized (MONITOR) {
   
                System.out.println("加锁成功....");
            }
        }
    }
}

通过这段代码的分析,可以看出,循环内部每次都对同一个对象进行加锁和解锁,对于这种一串零碎的操作都对同一个对象加锁情况,虚拟机将会把加锁同步的范围扩展 (粗化)到整个操作序列的外部。以上述代码为例,也就是扩展到把for循环这个操作加锁,这样只需要加锁一次就可以了。

总结

所以,通过本篇文章可以看出,jdk对synchronized 其实进行了一系列的优化来尽可能减少加锁时对于性能的消耗,包括锁升级、锁消除、锁粗化。希望通过本篇文章可以让你对synchronized 底层又有一个全新的认识。

PS:如果觉得这篇文章对你有帮助,欢迎大家关注公众号三友的java日记、分享、点赞、在看,感谢支持。

往期热门文章推荐

如何去阅读源码,我总结了18条心法

如何写出漂亮代码,我总结了45个小技巧

三万字盘点Spring/Boot的那些常用扩展点

三万字盘点Spring 9大核心基础功能

万字+20张图剖析Spring启动时12个核心步骤

1.5万字+30张图盘点索引常见的11个知识点

两万字盘点那些被玩烂了的设计模式

搜索关注公众号 三友的java日记 ,及时干货不错过,公众号致力于通过画图加上通俗易懂的语言讲解技术,让技术更加容易学习,回复 面试 即可获得一套面试真题。

相关文章
|
7月前
|
存储 安全 Java
Synchronized 优化
Synchronized 优化
|
7月前
|
Java
学习多线程之synchronized记录
学习多线程之synchronized记录
49 0
|
10天前
Synchronized锁原理和优化
Synchronize是通过对象头的markwordk来表明监视器的,监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换要从用户态切换为核心态,成本很高,此时这种锁叫重量级锁,在JDK1.6以后引入了偏向锁、轻量级锁、重量级锁 偏向锁:当一段代码没有别的线程访问,此时线程去访问会直接获取偏向锁 轻量级锁:当锁是偏向锁时,有另外一个线程来访问,偏向锁会升级为轻量级锁,这个线程会通过自旋方式不断获取锁,不会阻塞,提高性能 重量级锁:轻量级锁自旋一段时间后线程还没有获取到锁,线程就会进入阻塞状态,该锁会升级为重量级锁,重量级锁时,来竞争锁的所有线程都会阻塞,性能降低 注意,锁只能升
24 5
|
7月前
|
C++
多线程(锁策略, synchronized 对应的锁策略)
多线程(锁策略, synchronized 对应的锁策略)
55 2
|
安全 Java 调度
PCB状态字段细分,线程安全问题,加锁,synchronized
PCB状态字段细分,线程安全问题,加锁,synchronized
|
Java 编译器
synchronized关键字(作用 + 特点 + 锁升级 + 锁优化 + 与 volatile 对比)
1. synchronized 的作用 1)保证原子性 2)保证内存可见性 3)保证有序性 2. synchronized 特点 3. 锁升级的过程 1)偏向锁 2)轻量级锁 3)重量级锁 4. 锁的优化操作 1)锁消除 2)锁粗化 5. synchronized 使用示例 1)修饰普通方法:锁当前实例对象 2)修饰静态方法:锁当前类对象 3)修饰代码块:指定锁哪个对象 6. volatile 的作用 1)保证内存可见性 2)保证有序性 7. synchronized 和 volatile 的区别
102 0
synchronized关键字(作用 + 特点 + 锁升级 + 锁优化 + 与 volatile 对比)
|
存储 Java
synchronized锁升级原理
synchronized锁升级原理
265 1
synchronized锁升级原理
|
缓存 Java 数据库
synchronized锁升级的过程
之前只是了解过一些悲观锁的底层原理,和他具体是如何锁住线程的一些细节,正好今天休息,结合一些文章和自己的实践操作,整理成了一篇关于synchronized锁升级的过程,希望能对大家有所帮助.
188 0
synchronized锁升级的过程
|
Java
【synchronized】同步方法与同步块
【synchronized】同步方法与同步块
115 0
【synchronized】同步方法与同步块
|
安全 Java 编译器
多线程安全问题原理和解决办法Synchronized和ReentrantLock使用与区别
多线程安全问题原理和解决办法Synchronized和ReentrantLock使用与区别