码迷,mamicode.com
首页 > 其他好文 > 详细

设计模式:单例模式

时间:2020-09-09 19:08:14      阅读:39      评论:0      收藏:0      [点我收藏+]

标签:turn   进入   代码块   on()   多线程   另一个   ble   ssi   desc   

一.

  单例模式指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

二.饿汉模式

public class HungrySingleton {

    private HungrySingleton(){}

    private final static HungrySingleton HUNGRY_SINGLETON ;

    static {
        HUNGRY_SINGLETON = new HungrySingleton();
    }

    public static HungrySingleton getInstance(){
        return HUNGRY_SINGLETON;
    }
}

 饿汉式是指类的被加载的时候,就被初始化,并创建单例对象,不会存在访问安全问题。

缺点:所有的饿汉单例对象都会在项目启动时初始化,会造成大量内存资源浪费。

三.懒汉模式

(1)考虑到饿汉模式的缺点后,加以修改,在类被调用的时候,才初始化。

public class LazySimpleSingleton {

    private LazySimpleSingleton(){}

    private static LazySimpleSingleton LAZY_SIMPLE_SINGLETON = null;

    public static LazySimpleSingleton getIn,stance(){
        if(null==LAZY_SIMPLE_SINGLETON){
            LAZY_SIMPLE_SINGLETON = new LazySimpleSingleton();
            return LAZY_SIMPLE_SINGLETON;
        }
        return LAZY_SIMPLE_SINGLETON;
    }
}

 但是会带来一个新的问题,就是在多线程环境下,有两个线程同一时间进入getInstance方法,同事满足null=LAZY_SIMPLE_SINGLETON时,会创建两个对象,然后后创建的会覆盖先创建的单例对象。

考虑到这个问题后,进一步优化,使用synchronized关键字,给方法加锁。

public class LazySimpleSingleton {

    private LazySimpleSingleton(){}

    private static LazySimpleSingleton LAZY_SIMPLE_SINGLETON = null;

    public synchronized static LazySimpleSingleton getInstance(){
        if(null==LAZY_SIMPLE_SINGLETON){
            LAZY_SIMPLE_SINGLETON = new LazySimpleSingleton();
            return LAZY_SIMPLE_SINGLETON;
        }
        return LAZY_SIMPLE_SINGLETON;
    }
}

 当一个线程调用getInstance方法时,另一个线程也调用该方法,会出现阻塞,直到第一个线程执行结束,才继续调用,完美解决了线程安全问题。

出现新的问题:

如果线程数量暴增,给getInstatnce加锁,只有一个线程运行该方法,其他线程全部阻塞等待,用户体验不好。新的解决方案--双重检查锁单例写法应运而生。

(2)双重检查锁单例  (进门安检一次,闸口再检查一次)

改造一下写法:

public class LazyDoubleSingleton {
    private volatile static LazyDoubleSingleton instance;
    private LazyDoubleSingleton(){}

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

 这样的写法,其实和上一种写法差不多,都会造成大量线程阻塞。那如果把if条件往上升一级呢,先判断,再加锁。

public class LazyDoubleSingleton {
    private volatile static LazyDoubleSingleton instance;
    private LazyDoubleSingleton(){}

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

 经过此次修改后,还是会出现线程安全问题。

因为当两个线程同事满足null==instance条件后,会执行sychronized代码块的代码,该对象还是会被创建两次。

再优化一下,我们在sychronized代码块中再进行对象的非空检查,这样该对象就不会被创建两次。

public class LazyDoubleSingleton {
    private volatile static LazyDoubleSingleton instance;
    private LazyDoubleSingleton(){}

    public static LazyDoubleSingleton getInstance(){
        //检查是否要阻塞
        if(null==instance){
            synchronized (LazyDoubleSingleton.class){
                //检查是否要创建对象
                if(null==instance){
                    instance = new LazyDoubleSingleton();
                }

            }
        }
        return instance;
    }
}

四.静态内部类单例的写法

  双重检查锁单例这种方式虽然解决了线程安全问题和性能问题,但是用到了sychronized总是要上锁,对性能还是有一些影响。我们可以采用静态内部类的方式进行优化。

/**
 * @Author wen.jie
 * @Description 使用InnerClassSingleton类时,会默认先初始化内部类,如果没有使用,则内部类不初始化
 **/
public class InnerClassSingleton {

    private InnerClassSingleton(){}

    public static InnerClassSingleton getInstance(){
        return InnerClass.INNER_CLASS_SINGLETON;
    }

    private static class InnerClass{
        private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
    }
}

 这种方式兼顾了饿汉单例写法的内存浪费问题和sychronized的性能问题,内部类一定要在方法调用之前就被初始化,巧妙的避开了线程安全问题。静态内部类单例写法真的完美了吗?(未完待续)

设计模式:单例模式

标签:turn   进入   代码块   on()   多线程   另一个   ble   ssi   desc   

原文地址:https://www.cnblogs.com/wwjj4811/p/13581741.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!