单例模式线程是否安全?

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

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

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

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);

    }

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

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




猜你喜欢

转载自blog.csdn.net/weixin_45262118/article/details/108519818