单例模式线程是否安全?

killads
发布于 2020-9-18 15:41
浏览
0收藏

饿汉式缺点: 可能会浪费内存 线程安全

 

懒汉式缺点:只有在单线程下才有效 多线程下会破坏单例模式 线程不安全

 

饿汉是线程安全的,我们在这不讨论他,主角是我们的懒汉式!!

public class LazyMan{
    //私有化构造方法 
	private LazyMan(){
        
    }
    //创建一个对象 不赋值
    private static LazyMan lazyMan;
    
    //对外提供方法
    public static LazyMan getInstance(){
        if(lazyMan==null){//(1)
            try{
            //为了更好的体现 多线程 睡眠使所有线程都进入
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
            
            lazyMan=new LazyMan();
        }
        return lazyMan;
    }
    public static void main(String[] args){
        for(int i=0;i<10;i++){
            new Thread(()->{
            LazyMan.getInstance();
        	}).start();
        }
    }
}
结果==> 会创建10个对象

 

懒汉式线程不安全的原因:

线程不安全的原因:
假设有两个线程A和B A线程到达(1)处时判断lazyMan对象为null此时cpu的执行权被线程B抢去
但A还没有对lazyMan进行实例化
B读取lazyMan对象的状态仍然是null 线程AB都会进入if中通过new 创建两个不同的对象


解决方案:

 

线程不安全 就给它加锁

 private volatile static LazyMan2 lazyMan;
//解决方案  双重检测锁的懒汉式单例 DCL
    public static LazyMan1 getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan1.class) {
                if (lazyMan == null) {// (1)处
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lazyMan = new LazyMan1(); // 不是原子性操作
                    /*
                     * 1.分配内存空间
                     * 2.执行构造方法 初始化对象
                     * 3.把这个对象指向这个空间
                     * volatile只保证可见性 不保证原子性的,但是可以防止指令重排
                     * */
                }
            }
        }
        return lazyMan;
    }

 

还可以用反射暴力破解

public static void main(String[] args) throws Exception {
        //反射
        LazyMan2 instance1 = new LazyMan2();
        Constructor<LazyMan> declaredConstructor = 
            LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true); //无视私有的构造器
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);

    }

 

都会使得懒汉式的单例被破坏 创建多个对象!

 

##枚举也是线程安全的 即便使用反射也不能破坏其单例模式!!!

 

枚举的不能破坏单例的方法这里不提.

 

有关枚举的单例请移步下一篇博客!

 

 

 

作者:____小明同学i

来源:CSDN

分类
标签
收藏
回复
举报
回复
    相关推荐