专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

懒加载成员变量时的线程安全问题

问题场景

  在平时写代码的时候,我们有时候经常会遇到一些懒加载的成员变量的场景,比如下面这个例子


public class Test { private static Member member; public static Member getMember() { if (member == null) { member = new Member(30, "Jack"); } return member; } public static class Member { private String name; private int age; public Member(int age, String name) { this.age = age; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } }

  需要注意的是new Member(30, “Jack”);new一个对象出来这个动作一共是分三部分,如下:

1、 申请一块内存空间
2、 在这块内存空间上初始化Member对象
3、 将member对象的引用指向目标内存空间

  上述三个步骤中,2和3有可能会发生指令重排序,发生指令重排之后,new 一个 Member对象出来的步骤就变成了1->3->2。假设Test.member还没有被初始化,且此时有两个线程同时访问Test.getMember方法,线程1先进入了if (member == null)里,去new一个Member对象,这个时候恰好执行完了,1,3两步骤,还没有执行步骤2。此时线程2进来了,开始判断if (member == null),此时member== null 为false,所以线程2不走到if逻辑里而是继续往下走并返回还没有初始化完的member对象,如果在线程1执行步骤2之前,线程2里用拿到的member对象去执行操作就会有问题。

复现

  没复现出来,日了狗了,求求你new对象的时候重排序吧,我透。

解决方式

  为了避免new对象时指令重排序造成的赖加载成员变量时的线程安全问题,第一个想到的解决方法,给member加上关键字volatile,防止member在通过new赋值时出现指令重排序:

方案1:

    private static volatile Member member;

    public static Member getMember() {
        if (member == null) {
            member = new Member(30, "Jack");
        }

        return member;
    }

但是这样子还是不够,多线程的情况下,会new出多个对象来,虽然不影响代码的运行,但是还是有一点点浪费内存啊。   在方案2的基础上进行优化,将getMember编程同步方法:

方案2:

    private static volatile Member member;

    public static synchronized Member getMember() {
        if (member == null) {
            member = new Member(30, "Jack");
        }

        return member;
    }

  这样子方案2确实解决了方案1中并发情况下可能会在堆中new出多个Member对象的问题,但是将整个懒加载的方法设置为同步方法,开销实在是太大了,即使在JDK8中,synchronized一开始不会马上是重型锁,而是从偏向锁->轻型锁->自旋锁->重型锁一步步提升锁的等级。   那么如何才能优化同步方法呢?——>缩小同步锁的粒度&double-check,代码优化如下:

方案3:

    private static volatile Member member;

    public static Member getMember() {
        if (member == null) {
            synchronized (Test.class) {
                if (member == null) { //Double-check,保证member还未被初始化
                    member = new Member(30, "Jack");
                }
            }
        }

        return member;
    }

文章永久链接:https://tech.souyunku.com/26339

未经允许不得转载:搜云库技术团队 » 懒加载成员变量时的线程安全问题

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们