单例模式5种常见写法

今天总结一下单利模式,平常用的也比较多,但是深入研究还是最近一段时间,学习总结,单利目前有以下几种写法

饿汉式单例:

饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象。绝对线程安全,在线 程还没出现以前就是实例化了,不可能存在访问安全问题。 优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好。 缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费了内存。

写法如下

public class HungrySingleton {
    //创建一个静态私有的自己,在类加载的时候及创建
    private static final HungrySingleton hungrySingleton = new HungrySingleton();

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

大部分时候很少用,只有在知道必须要用到单利模式的时候才这个写。

懒汉式单例:在需要使用的时候才开始加载,为了线程安全需要加锁

public class LazySingleton {
    //volatile 多线程会回到主线程读写该对象,保证数据的一致性。但是不能保证顺序性
    private volatile static LazySingleton lazySingleton ;

    public LazySingleton() {

    }
    //比较常见的写法
    public static LazySingleton getInstance(){
        if ( lazySingleton == null){
            //加锁 防止多线程 生成多个实例对象
            synchronized (LazySingleton.class){
                if (lazySingleton == null){
                    lazySingleton = new LazySingleton();
                }
            }

        }

        return lazySingleton;
    }
}

还有一种目前比较流行的写法,利用静态内部类的原理来创建单例模式

public class SecLazySingleton {

   public SecLazySingleton() {
        if (Insideton.lazy != null){
            throw new RuntimeException("不允许反射创建该实例");
        }
    }

    
    //static final 保证的唯一性
    public static final SecLazySingleton getInstance(){
        return Insideton.lazy;
    }

    //在调用的时候内部类才加载生成实例对象
    private static class Insideton{
        private static final SecLazySingleton lazy = new SecLazySingleton();
    }

      /**防止被反序列化
    //原因序列化时 在jdk 中ObjectInputStream类中的readObject方法
    //---》调用readObject0()方法----》调用了 ObjectInputStream 的 readOrdinaryObject()---》
     ObjectStreamClass 的 isInstantiable()
     boolean isInstantiable() { requireInitialized(); return (cons != null);
     }判断实例对象构造方法是否为空,不为空就实例 然后回到ObjectInputStream 的 readOrdinaryObject()
     有一段
     if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod())
     //hasReadResolveMethod()方法
     boolean hasReadResolveMethod() { requireInitialized();
     return (readResolveMethod != null);
     }

     ObjectStreamClass()方法中给 readResolveMethod 进行赋值
     readResolveMethod = getInheritableMethod( cl, "readResolve", null, Object.class);
     。。。。。。。。。。。。。。。。。后面一系列的逻辑
     其实序列化的时候就创建了2次,新创建的对象没有返回,还是使用以前的对象,一般情况下不会去这么干序列化单利
     **/
    private Object readResolve(){
        return Insideton.lazy;
    }
}

jdk再设计的时候考虑到了单例会被各种破坏,于是利用jdk的规则,有人发明了

注册式单例:

每一个实例都登记到某一个地方,使用唯一的标 识获取实例.

public enum EnumSingleton {
    INSTANCE;

    private Object data; public Object getData() {
        return data;
    }

    public void setData(Object data) { this.data = data;
    }

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

容器式单例(注册式单例的第二种写法)

public class ContainerSingleton {
    
    public ContainerSingleton() {
    }

    private static Map ioc = new ConcurrentHashMap<>();
    
    //不敢保证线程安全
    public static Object getBean(String className){
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj); } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className); }
        }
    }

}

个人平时写的话 都比较喜欢第二种和第三种,既保证了线程安全,内存消耗相对不会太浪费。

你可能感兴趣的:(java设计模式)