Java并发学习之安全发布对象(一)

首先要知道为什么要安全发布对象?

当对象发布时有可能被别的线程修改。

直接上例子

private String [] exampleString = {"1","2"};

public String [] getExampleString() {

return exampleString;

}

public static void main(String[] args) {

IssuedExample1 issuedExample1 =new IssuedExample1();

log.info("初始化:{}", Arrays.toString(issuedExample1.getExampleString()));

issuedExample1.getExampleString()[1] ="11";

log.info("修改后:{}",Arrays.toString(issuedExample1.getExampleString()));

}

就是我们可以看见 我们提供了一个 exampleString 数组 然后提供一个Get 方法。 在下面main方法做了一个什么操作? 可以看见实例化出了此对象,我们发布完成对象,初始化输出结果为     初始化:[1, 2]    修改后为 修改后:[1, 11]  , 可以看见 我们无法确定在初始化对象的时候 这个对象不被别的线程篡改。

所以这样去发布对象 可能会被别的线程修改值。导致我们预期的值和实际取到的值不同。

我们都知道单例模式发布对象。

// 懒汉模式 在第一次使用时进行创建

    /**

    * 私有构造方法

    */

    private IssuedExample2() {

}

//单例对象

    private static IssuedExample2 instance =null;

/**

    * @return

    */

    private static IssuedExample2 getInstance(){

if(instance ==null){   // 如果两个线程同时运行到这 现在安全吗?  

instance =new IssuedExample2(); // 两个线程会同时实例化这个对象 

}

return  instance;

}

上面贴出的代码中给出了疑问,也给出了答案。 

那么 饿汉模式会怎么样?

//饿汉模式

/**

* 私有构造方法

*/

private IssuedExample3() {

}

/**

* 初始化对象

*/

private static IssuedExample3 instance =new IssuedExample3();

/**

* @return

*/

private static IssuedExample3 getInstance(){

return  instance;

}

饿汉模式不会出现线程问题 在类装载时进行创建,当构造方法中处理一些逻辑过于复杂并没有使用可能会造成资源的浪费。 

如果饿汉模式是线程安全的 那么我们可以让懒汉模式线程安全吗? 因为懒汉模式是在第一次调用时进行创建,

饿汉模式则是类装载时进行创建,什么意思呢? 就是饿汉是一开始就加载不管你用不用我,我都创建,懒汉是你使用我我才进行创建对象。

改进一下懒汉模式,下面会贴出几种改进的方法。

/**

*1. 方法前加synchronized  确保同一时间只有一根线程能执行此方法 懒汉模式安全发布对象 

* 线程安全

*/

private static synchronized IssuedExample2 getInstance(){

if(instance ==null){

instance =new IssuedExample2();

}

return  instance;

}






/**

* 双重检测   线程不安全

* @return

*/
private static IssuedExample2 getInstance(){

if(instance ==null){

synchronized(IssuedExample2.class){

if(instance ==null){

instance =new IssuedExample2();

}

}

}

return  instance;

}

问题来了 上面为什么就不安全了???

当一根线程正在执行到instance =new IssuedExample2(); 时,另外一根线程出来了 他在执行第一个判断if(instance ==null)  这个时候 他发现 不为空 就直接返回对象。但是此对象并未执行完成初始化对象。

为什么第二根线程为什么会得到对象不为空的实例(实则是为空的实例)

这么说 在instance =new IssuedExample2(); 

会进行三步操作

第一步 在内存中分配空间

第二步 初始化对象

第三步 初始化对象指向内存分配空间

ok!这里没问题,那么问题出在哪?

 JVM和cpu优化,发生了指令重排。

所以可能造成这样的情况

第一步 在内存中分配空间

第二步 初始化对象指向内存分配空间 

//也就是这里 我们第二线程执行判空处理 发现 引用有指向的内存地址也就直接进行返回了 重点 引用有了对象地址!!!

第三步 才去初始化对象 

未完待续。。。。


本文仅限本人小白学习参考,不足之处请大佬指正。

你可能感兴趣的:(Java并发学习之安全发布对象(一))