JAVA中的单例模式分析(doublecheck和枚举实现)

    技术2022-07-10  142

    文章目录

    1.饿汉模式2.懒汉模式3.更好的解决办法 在java中,单例模式的实现方法有如下几种:

    1.饿汉模式

    所为饿汉模式,即一开始就创建一个静态的对象,之后该对象一直存在。这种模式不会有线程安全问题。

    package com.dhb.builder.singleton; public class Singleton1 { private static Singleton1 instance = new Singleton1(); private Singleton1() { } public static Singleton1 getInstance() { return Singleton1.instance; } }

    2.懒汉模式

    对于饿汉模式,优点在于实现简单。但是存在一个问题就是 instance 只要 Singleton1被加载就会被创建到static所在的静态方法区。如实现数据库连接池等情况,用这种方法一上来就要创建数据库的连接资源,实际系统中暂不使用。这就造成了资源的浪费。因此,对于这种情况,出现了与之对应的懒汉模式。 即一开始并不创建对象,待需要使用时再new。

    package com.dhb.builder.singleton; public class SingletonDemo1 { private static SingletonDemo1 instance = null; private SingletonDemo1() { } /** * 存在线程安全问题 * @return */ public static SingletonDemo1 getInstance() { if(instance == null) { instance = new SingletonDemo1(); } return instance; } }

    这是大家想到的最常用的懒汉模式的写法。但是问题来了,上述模式在多线程的情况下是线程不安全的!也就是说,如果有两个线程,同时getInstance(),同时都会判断instance的值为null。这种情况下会创建多个实例。 为了解决上述问题,我们引入了锁:

    package com.dhb.builder.singleton; public class SingletonDemo2 { private static SingletonDemo2 instance = null; private SingletonDemo2() { } /** * 增加同步机制,解决线程安全 * @return */ public static synchronized SingletonDemo2 getInstance() { if(instance == null) { instance = new SingletonDemo2(); } return instance; } }

    上面这种做法,确实解决了线程安全问题,但是带来了一个更加不好的问题,那就是每一次请求都会加锁!这样会严重影响性能。更好的做法是采用双重检查机制:

    package com.dhb.builder.singleton; public class SingletonDemo3 { private static SingletonDemo3 instance = null; private SingletonDemo3() { } /** * 增加双重检查机制,解决synchronized效率问题 * @return */ public static SingletonDemo3 getInstance() { if(instance == null) synchronized (SingletonDemo3.class) { if (instance == null) { instance = new SingletonDemo3(); } } return instance; } }

    上述单例实际仍然存在问题,那就是类初始化仍然需要时间,如果同时又两个线程同时进入getInstance方法,第一个线程锁定之后,第二个线程判断不为空,则直接使用instalce,如果此时第一个线程对SingletonDemo3对象还没实例化完成,如该对象内部存在一个耗时的引用,如果是一个数据库连接,则会导致第二个线程使用的对象不完整。出现空指针。因此更好的写法是加上volatile。以保证happen-before原则。

    package com.dhb.builder.singleton; public class SingletonDemo4 { private volatile static SingletonDemo4 instance = null; private SingletonDemo4() { } /** * 增加双重检查机制,解决synchronized效率问题 * @return */ public static SingletonDemo4 getInstance() { if(instance == null) synchronized (SingletonDemo4.class) { if (instance == null) { instance = new SingletonDemo4(); } } return instance; } }

    这样单例模式才完全解决。上述方法比较冗繁,有没有更好的解决办法呢,有幸阅读过《effective java》这本书对于单例有更好的解决办法。

    3.更好的解决办法

    第一种方法,利用静态内部类:

    package com.dhb.builder.singleton; import java.util.stream.IntStream; public class SingletonHolder { private SingletonHolder() { } private static class InstanceHolder{ private final static SingletonHolder INSTANCE = new SingletonHolder(); } public static SingletonHolder getInstance() { return InstanceHolder.INSTANCE; } public static void main(String[] args) { IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){ @Override public void run() { System.out.println(SingletonHolder.getInstance()); } }.start()); } }

    上述方法执行结果:

    com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6 com.dhb.builder.singleton.SingletonHolder@55c1b7a6

    可以看出SingletonHolder类只实例化了一次。这种方法很巧妙地利用一个内部类,很简单的代码即实现了单例,而且是线程安全。

    方式二:《effective java》中还有一种更简单的写法,那就是枚举。也是《effective java》作者最为推崇的方法。

    package com.dhb.builder.singleton; import java.util.stream.IntStream; public class SingletonEnum { private SingletonEnum() { } private enum Singleton { INSTANCE; private final SingletonEnum instance; Singleton() { instance = new SingletonEnum(); } public SingletonEnum getInstance() { return instance; } } public static SingletonEnum getInstance() { return Singleton.INSTANCE.getInstance(); } public static void main(String[] args) { IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){ @Override public void run() { System.out.println(SingletonEnum.getInstance()); } }.start()); } }

    上述方法执行结果:

    com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6 com.dhb.builder.singleton.SingletonEnum@55c1b7a6

    在java中,枚举天然实现了单例模式。其构造方法只会实例化一次。

    Processed: 0.011, SQL: 9