安全发布对象-发布与溢出

#不安全发布对象

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

package com.mmall.concurrency.example.publish;

import lombok.extern.slf4j.Slf4j;

import javax.annotation.concurrent.NotThreadSafe;
import java.util.Arrays;

@Slf4j
@NotThreadSafe
public class UnsafePublish {

    private String[] states={"a","b","c"};
    public String[] getStates(){
        return states;
    }

    private class OtherThread extends Thread{
        private String[] states;

        OtherThread(String[] states){
            this.states=states;
        }

        @Override
        public void run() {
            states[0]="d";
        }
    }


    public static void main(String [] args){
        UnsafePublish unsafePublish=new UnsafePublish();
        log.info("{}", Arrays.toString(unsafePublish.getStates()));

        OtherThread otherThread=unsafePublish.new OtherThread(unsafePublish.getStates());
        otherThread.run();

        log.info("{}", Arrays.toString(unsafePublish.getStates()));

    }
}

由于对象中成员变量的值会被其他线程修改,所以当一个线程修改变量后,变量的值也很可能变成其他线程修改的值。

对象溢出

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

public class Escape {
    private int thisCanBeEscape=0;

    public Escape(){
        new InnerClass();
    }

    private class InnerClass {
        public InnerClass(){
            log.info("{}",Escape.this.thisCanBeEscape);
        }
    }

    public static void main(String [] args){
        new Escape();
    }
}

安全发布对象

1.在静态初始化函数中初始化一个对象索引

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

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

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

一个线程不安全的单例懒汉式发布对象

package com.mmall.concurrency.example.singleton;
import com.mmall.concurrency.annoations.NotRecommend;
import javax.annotation.concurrent.NotThreadSafe;

/*
* 懒汉式->双层同步锁单例模式
* */
@NotThreadSafe
public class SingletonExample4 {
    //私有构造函数(因为只有私有化了才能避免外界通过new的方式不断创建新的单例对象)
    private SingletonExample4(){

    }
    //单例对象
    private static SingletonExample4 instance=null;

    //静态工厂方法来获取单
    public static synchronized SingletonExample4 getInstance(){
        if(instance==null){
            synchronized(SingletonExample4.class){
                if(instance==null){//双层检测机制
                    instance=new SingletonExample4();
                }
            }
        }
        return instance;
    }
}

尽管有双层检测机制,但是线程仍是不安全的。
当我们创建一个对象时,jvm会进行指令重排。如果按着正常步骤创建对象是这样的:
1.memory=allocate()//分配内存空间
2.ctorInstance()//初始化对象
3.instance=memory//将引用指向分配的空间地址
由于jvm的指令重排,上面的顺序有可能是这样的
1.memory=allocate()//分配内存空间
3.instance=memory//将引用指向分配的空间地址
2.ctorInstance()//初始化对象

在这种情况下,假如有两个线程A、B,如果线程A执行到了

instance=new SingletonExample4();

并且jvm执行这段代码的顺序是按着1 3 2来的,那么当A在执行完1 3后2还没来得及执行初始化对象,线程B执行

 public static synchronized SingletonExample4 getInstance(){
        if(instance==null){
            synchronized(SingletonExample4.class){
                if(instance==null){//双层检测机制
                    instance=new SingletonExample4();
                }
            }
        }
        return instance;
    }
}

就会判断instance!=null从而直接获取到未被初始化的instance。

为了避免这种现象,我们使用volatile关键字修饰就可以避免这种情况。

package com.mmall.concurrency.example.singleton;

import com.mmall.concurrency.annoations.ThreadSafe;

import javax.annotation.concurrent.NotThreadSafe;

/*
* 懒汉式->双层同步锁单例模式
* */
@ThreadSafe
public class SingletonExample5 {
    //私有构造函数(因为只有私有化了才能避免外界通过new的方式不断创建新的单例对象)
    private SingletonExample5(){

    }
    //单例对象
    private static volatile SingletonExample5 instance=null;

    //静态工厂方法来获取单
    public static synchronized SingletonExample5 getInstance(){
        if(instance==null){
            synchronized(SingletonExample5.class){
                if(instance==null){
                    instance=new SingletonExample5();
                }
            }
        }
        return instance;
    }
}

使用枚举类来实现线程安全的单例模式

package com.mmall.concurrency.example.singleton;

import com.mmall.concurrency.annoations.Recommend;
import com.mmall.concurrency.annoations.ThreadSafe;

/*
* 枚举模式:线程最安全的
* */
@ThreadSafe
@Recommend
public class SingletonExample7 {
    //私有构造函数(因为只有私有化了才能避免外界通过new的方式不断创建新的单例对象)
    private SingletonExample7(){

    }

    //静态工厂方法来获取单
    public static  SingletonExample7 getInstance(){
        return Singleton.INSTANCE.getInstance();
    }

    private enum Singleton{
        INSTANCE;

        private SingletonExample7 singleton;

        //jvm保证这个方法绝对只调用一次
        Singleton(){
            singleton=new SingletonExample7();
        }

        public SingletonExample7 getInstance(){
            return singleton;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值