Java锁擦除与锁粗化概念和使用详解

目录
  • 一、什么是锁擦除
  • 二、锁擦除的演示
  • 三、什么是锁粗化
  • 四、锁粗化的演示

一、什么是锁擦除

锁擦除是指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行擦除。锁擦除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行。

二、锁擦除的演示

public class LockErasureDemo {
    public static void main(String[] args) {
        new Thread(() -> {
            String contact = contact("aa", "bb", "cc");
            System.out.println(Thread.currentThread().getName() + ":" + contact);
        }, "t1").start();
        new Thread(() -> {
            String contact = contact("dd", "ee", "ff");
            System.out.println(Thread.currentThread().getName() + ":" + contact);
        }, "t2").start();
    }
    private static String contact(String s1, String s2, String s3) {
        StringBuffer stringBuffer = new StringBuffer();
        return stringBuffer.append(s1).append(s2).append(s3).toString();
    }
}

观察上面的代码,我们都知道StringBuffer的append方法是加了synchronized的同步方法:

public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

这里锁住的对象其实就是stringBuffer这个局部变量,因为是局部变量,所以每个线程进来生成的stringBuffer对象不同,相当于每个线程自己new了一把锁,所以这里不存在竞争同一把锁的问题,JVM底层会将这个锁进行擦除。

三、什么是锁粗化

原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小,只在共享数据的实际作用域中才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待锁的线程也能尽快拿到锁。大部分情况下,上面的原则都是正确的,但是如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。

四、锁粗化的演示

public class LockCoarseningDemo {
    static Object objLock = new Object();
    public static void main(String[] args) {
        // 反复加锁、解锁
        new Thread(() -> {
            synchronized (objLock) {
                System.out.println("a");
            }
            synchronized (objLock) {
                System.out.println("b");
            }
            synchronized (objLock) {
                System.out.println("c");
            }
        }, "t1").start();
    }
}

观察上面的案例,同一个线程对同一把锁,反复不断地获取锁、释放锁,这样肯定影响性能。为了避免重复的加锁解锁,JVM可能会将上面的代码优化成下面这样:

// 锁粗化: 如果方法中首尾相接,前后相邻的都是同一个锁对象,那JIT编译器就会把这几个synchronized块合并成一个大块
// 加粗加大范围,一次申请锁即可,避免多次的申请和释放锁,提升性能
new Thread(() -> {
    synchronized (objLock) {
        System.out.println("a");
        System.out.println("b");
        System.out.println("c");
    }
}, "t1").start();

到此这篇关于Java锁擦除与锁粗化概念和使用详解的文章就介绍到这了,更多相关Java锁擦除与锁粗化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java 锁粗化与循环问题

    1. 写在前面 "JVM 解剖公园"是一个持续更新的系列迷你博客,阅读每篇文章一般需要5到10分钟.限于篇幅,仅对某个主题按照问题.测试.基准程序.观察结果深入讲解.因此,这里的数据和讨论可以当轶事看,并没有做一致性.写作风格.句法和语义错误.重复或一致性检查.如果选择采信文中内容,风险自负. Aleksey Shipilёv,JVM 性能极客 推特 @shipilev 问题.评论.建议发送到 aleksey@shipilev.net 译注:锁粗化(Lock Coarsening).锁

  • Java多线程死锁问题详解(wait和notify)

    目录 一. synchronnized 的特性 1. 互斥性 2. 可重入性 二. 死锁问题 1. 什么是死锁 2. 死锁的四个必要条件 3. 常见的死锁场景及解决 3.1 不可重入造成的死锁 3.2 循环等待的场景 三. Object类中提供线程等待的方法 1. 常用方法 2. wait和notify的搭配使用 3. wait 和 sleep 的区别 4. 练习: 顺序打印ABC 总结 一. synchronnized 的特性 1. 互斥性 synchronized 会起到互斥效果, 这里的互

  • Java锁擦除与锁粗化概念和使用详解

    目录 一.什么是锁擦除 二.锁擦除的演示 三.什么是锁粗化 四.锁粗化的演示 一.什么是锁擦除 锁擦除是指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行擦除.锁擦除的主要判定依据来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行. 二.锁擦除的演示 public class LockErasureDemo { publi

  • Java数据结构之图的基础概念和数据模型详解

    目录 图的实际应用 图的定义及分类 图的相关术语 图的存储结构 邻接矩阵 邻接表 图的实现 图的API设计 代码实现 图的实际应用 在现实生活中,有许多应用场景会包含很多点以及点点之间的连接,而这些应用场景我们都可以用即将要学习的图这种数据结构去解决. 地图: 我们生活中经常使用的地图,基本上是由城市以及连接城市的道路组成,如果我们把城市看做是一个一个的点,把道路看做是一条一条的连接,那么地图就是我们将要学习的图这种数据结构. 图的定义及分类 定义: 图是由一组顶点和一组能够将两个顶点相连的边组

  • Mybatis-Plus进阶分页与乐观锁插件及通用枚举和多数据源详解

    分页插件   MP中自带了分页插件的功能,只需要在配置类中进行简单的配置即可使用分页的相关功能.分页插件常常与前端的分页显示功能相关,为了在前端美观的显示查询到的数据,通常会使用分页插件,将所有的数据分成许多页一页一页的进行显示,不同页的切换使用按钮来完成 MP的插件配置类 @Configuration public class MybatisPlusConfiguration { @Bean public MybatisPlusInterceptor mybatisPlusIntercepto

  • java 中同步方法和同步代码块的区别详解

    java 中同步方法和同步代码块的区别详解 在Java语言中,每一个对象有一把锁.线程可以使用synchronized关键字来获取对象上的锁.synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁). 问题的由来: 看到这样一个面试题: //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized (obj){} } synchronized用于解决同步问

  • Java并发编程之同步容器与并发容器详解

    一.同步容器  1.Vector-->ArrayList vector 是线程(Thread)同步(Synchronized)的,所以它也是线程安全的: Arraylist是线程异步(ASynchronized)的,是不安全的: 2.Hashtable-->HashMap Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable: HashMap是非synchronized,这意味着HashMap是非线程安全的; 3.Coll

  • Java ThreadLocal原理解析以及应用场景分析案例详解

    目录 ThreadLocal的定义 ThreadLocal的应用场景 ThreadLocal的demo TheadLocal的源码解析 ThreadLocal的set方法 ThreadLocal的get方法 ThreadLocalMap的结构 ThreadLocalMap的set方法 ThreadLocalMap的getEntry方法 ThreadLocal的内存泄露 如何避免内存泄露呢 应用实例 实际应用二 总结 ThreadLocal的定义 JDK对ThreadLocal的定义如下: The

  • Java并发编程加锁导致的活跃性问题详解方案

    目录 死锁(Deadlock) 死锁的解决和预防 1.超时释放锁 2.按顺序加锁 3.死锁检测 活锁(Livelock) 避免活锁 饥饿 解决饥饿 性能问题 上下文切换 什么是上下文切换? 减少上下文切换的方法 资源限制 什么是资源限制 资源限制引发的问题 如何解决资源限制的问题 我们主要处理锁带来的问题. 首先就是最出名的死锁 死锁(Deadlock) 什么是死锁 死锁是当线程进入无限期等待状态时发生的情况,因为所请求的锁被另一个线程持有,而另一个线程又等待第一个线程持有的另一个锁 导致互相等

  • Java四个线程常用函数超全使用详解

    目录 前言 1. wait() 2. join() 3. sleep() 4. yield() 5. 总结 5.1 wait和join的区别 5.2 wait和sleep的区别 前言 之前没怎么关注到这两个的区别以及源码探讨 后面被某个公司面试问到了,开始查漏补缺 1. wait() 使当前线程等待,直到它被唤醒,通常是通过被通知或被中断,或者直到经过一定的实时时间. 本身属于一个Object 类,查看源代码也可知:public class Object { 查看其源码可知,一共有三个重载的方法

  • Java中读写锁ReadWriteLock的原理与应用详解

    目录 什么是读写锁? 为什么需要读写锁? 读写锁的特点 读写锁的使用场景 读写锁的主要成员和结构图 读写锁的实现原理 读写锁总结 Java并发编程提供了读写锁,主要用于读多写少的场景,今天我就重点来讲解读写锁的底层实现原理 什么是读写锁? 读写锁并不是JAVA所特有的读写锁(Readers-Writer Lock)顾名思义是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的. 所谓的读

  • Python Thread虚假唤醒概念与防范详解

    目录 什么是虚假唤醒 现在改用4个线程 使用20个线程同时跑 现在改用while进行判断 总结 什么是虚假唤醒 虚假唤醒是一种现象,它只会出现在多线程环境中,指的是在多线程环境下,多个线程等待在同一个条件上,等到条件满足时,所有等待的线程都被唤醒,但由于多个线程执行的顺序不同,后面竞争到锁的线程在获得时间片时条件已经不再满足,线程应该继续睡眠但是却继续往下运行的一种现象. 上面是比较书面化的定义,我们用人能听懂的话来介绍一下虚假唤醒. 多线程环境的编程中,我们经常遇到让多个线程等待在一个条件上,

随机推荐