ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

并发编程与高并发(一):安全发布对象之对象发布与对象逸出

2021-09-22 15:03:30  阅读:109  来源: 互联网

标签:SingleDoubleCheckLock instance 对象 private 并发 发布 static public


发布对象

使一个对象能够被当前范围之外的代码所使用的叫做发布对象

import java.util.Arrays;

/**
 * 多线程的场景下无法保证其他线程对states的修改,所以这个对象是线程不安全的
 */
public class PublishNotSafe {
    private String[] states = {"a", "b", "c"};

    public String[] getStates() {
        return states;
    }

    public static void main(String[] args) {
        PublishNotSafe p = new PublishNotSafe();
        System.out.println("1-" + Arrays.toString(p.getStates()));
        // 1-[a, b, c]
        p.getStates()[0] = "修改之后的值";

        System.out.println("2-" + Arrays.toString(p.getStates()));
        // 2-[修改之后的值, b, c]
    }
}

那么如何保证对象发布的安全性呢,有以下四种方法

在静态初始化函数中初始化一个对象引用

将对象的引用保存到volatile类型或者AtomicReference对象中

将对象的引用保存到某个正确构造对象的final类型域中

将对象的引用保存到一个由锁保护的域中

安全发布对象的四种方法

下面以单例模式来说明如何安全发布对象

1.饿汉式单例

/**
 * 饿汉式单例,线程安全
 * 但是每次类初始化的时候就执行构造方法,可能会导致性能下降
 */
public class SingleHungry {
    private static final SingleHungry SINGLE_HUNGRY = new SingleHungry();

    private SingleHungry() {

    }

    public static SingleHungry getSingleHungryInstance() {
        return SINGLE_HUNGRY;
    }
}

2.懒汉式单例

/**
 * 懒汉式单例,线程不安全
 * 调用的时候才会发生初始化,性能略高
 */
public class SingleLazy {
    private static SingleLazy SINGLE_LAZY ;

    private SingleLazy() {

    }

    public static SingleLazy getSingleLazyInstance() {
        // 问题出现在这里,当多线程的场景下没有保证线程安全
        if (SINGLE_LAZY == null) {
            SINGLE_LAZY = new SingleLazy();
            return SINGLE_LAZY;
        }
        return SINGLE_LAZY;
    }
}

3.懒汉式单例 + 双检锁

/**
 * 双检锁
 */
public class SingleDoubleCheckLock {
    private static SingleDoubleCheckLock instance;

    private SingleDoubleCheckLock() {

    }

    // 1.memory = allocate() 分配对象的内存空间
    // 2.ctorInstance() 初始化对象
    // 3.instance = memory 设置instance指向刚分配的内存

    // JVM和CPU进行优化,发生了指令重排序

    // 1.memory = allocate() 分配对象的内存空间
    // 3.instance = memory 设置instance指向刚分配的内存
    // 2.ctorInstance() 初始化对象

    public static SingleDoubleCheckLock getInstance() {
        if (instance == null) {  // 双重检测机制              // B线程执行到这里的时候发现A已经执行到new对象的步骤直接return了一个没有指针的对象
            synchronized (SingleDoubleCheckLock.class) {    // 同步锁
                if (instance == null) {
                    instance = new SingleDoubleCheckLock(); // A线程 - 3
                }
            }
        }
        return instance;
    }
}

4.懒汉式单例 + 双检锁 + volatile

/**
 * 双检锁
 */
public class SingleDoubleCheckLock {
    private static volatile SingleDoubleCheckLock instance;

    private SingleDoubleCheckLock() {

    }

    // 1.memory = allocate() 分配对象的内存空间
    // 2.ctorInstance() 初始化对象
    // 3.instance = memory 设置instance指向刚分配的内存

    public static SingleDoubleCheckLock getInstance() {
        if (instance == null) {
            synchronized (SingleDoubleCheckLock.class) {
                if (instance == null) {
                    instance = new SingleDoubleCheckLock();
                }
            }
        }
        return instance;
    }
}

5.枚举单例

public class SingleEnum {

    public static void main(String[] args) {
        SingleEnum s1 = SingleEnum.getInstance();
        SingleEnum s2 = SingleEnum.getInstance();
        System.out.println(s1);
        System.out.println(s2);
    }

    private SingleEnum() {

    }

    public static SingleEnum getInstance() {
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton {
        INSTANCE;
        private SingleEnum singleton;
        
        // JVM保证这个方法只会被调用一次
        Singleton() {
            singleton = new SingleEnum();
        }

        public SingleEnum getInstance() {
            return singleton;
        }
    }

}

对象逸出

一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见

/**
 * 在EscapeNotSafe没有被正确构造完成时就已经被InnerClass引用到了,可能会出现问题
 * thread2在EscapeNotSafe没有完全发布之前就已经看到它了
 * 不应该直接new InnerClass(),应该采用专有的init方法
 */
public class EscapeNotSafe {
    private int thisCanBeEscape = 0;

    public EscapeNotSafe() {
        // thread2
        new InnerClass();
    }

    private class InnerClass {
        public InnerClass() {
            System.out.println(EscapeNotSafe.this.thisCanBeEscape);
        }
    }

    public static void main(String[] args) {
        // thread1
        new EscapeNotSafe();
    }
}

标签:SingleDoubleCheckLock,instance,对象,private,并发,发布,static,public
来源: https://blog.csdn.net/qq_42163058/article/details/120410822

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有