Java 单例模式 五种常见的写法

来源:互联网 发布:轨道交通大数据平台 编辑:程序博客网 时间:2024/05/01 07:42

Java 单例模式 五种常见的写法

image

懒汉

/** * 懒汉,线程不安全 * 由私有构造器和一个公有静态工厂方法构成,在工厂方法中对singleton进行null判断,如果是null就new一个出来,最后返回singleton对象 * 这种方法可以实现延时加载(lazy landing),但是有一个致命弱点:线程不安全。如果有两条线程同时调用getSingleton()方法,就有很大可能导致重复创建对象。 * Created by gongzi on 2017/2/13. */public class LazySingleton {    private static LazySingleton instance;    private LazySingleton() {    }    public static LazySingleton getInstance() {        if (instance == null) {            instance = new LazySingleton();        }        return instance;    }}

饿汉

/** * 饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建 * 好处是编写简单,但是无法做到延迟创建对象 * Created by gongzi on 2017/2/13. */public class HungerSingleton {    private static HungerSingleton instance = new HungerSingleton();    private HungerSingleton() {    }    public static HungerSingleton getInstance() {        return instance;    }}

双重校验锁

/** * 双重检查锁定 * 这种写法考虑了线程安全,将对singleton的null判断以及new的部分使用synchronized进行加锁。 * 同时,对singleton对象使用volatile关键字进行限制,保证其对所有线程的可见性,并且禁止对其进行指令重排序优化。 * 在单例中new的情况非常少,绝大多数都是可以并行的读操作。因此在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,执行效率提高的目的也就达到了 * 注意:双重检查锁法,不能在jdk1.5之前使用, * 而在Android平台上使用就比较放心了,一般Android都是jdk1.6以上,不仅修正了volatile的语义问题,还加入了不少锁优化,使得多线程同步的开销降低不少 * Created by gongzi on 2017/2/13. */public class DoubleValidateSingleton {    private volatile static DoubleValidateSingleton singleton;    private DoubleValidateSingleton() {    }    public static DoubleValidateSingleton getSingleton() {        if (singleton == null) {            synchronized (DoubleValidateSingleton.class) {                if (singleton == null) {                    singleton = new DoubleValidateSingleton();                }            }        }        return singleton;    }}

枚举

/** * 这种方式是[Effective Java - Joshua Bloch](https://www.amazon.cn/dp/B01DPUXUWG/ref=sr_1_2?ie=UTF8&qid=1486966767&sr=8-2&keywords=Effective+Java)提倡的方式, * 使用枚举,除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。 * 因此,Effective Java 推荐尽可能地使用枚举来实现单例。 * 注意:枚举,虽然Effective Java中推荐使用,但是在Android平台上却是不被推荐的。 * <p> * Created by gongzi on 2017/2/13. */public enum EnumSingleton {    INSTANCE;    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

静态内部类

/** * 这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别): * 第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果), * 而这种方式是Singleton类被装载了,instance不一定被初始化。 * 因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。 * 想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化, * 因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。 * 这个时候,这种方式相比第三和第四种方式就显得很合理。 * Created by gongzi on 2017/2/13. */public class StaticInnerClassSingleton {    private StaticInnerClassSingleton() {    }    public static final StaticInnerClassSingleton getInstance() {        return SingletonHolder.INSTANCE;    }    private static class SingletonHolder {        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();    }}

参考资料

  1. 你真的会写单例模式吗——Java实现
  2. http://cantellow.iteye.com/blog/838473
  3. 《Effective Java(第二版)》
  4. 《深入理解Java虚拟机——JVM高级特性与最佳实践(第二版)》
  5. Java:单例模式的七种写法
1 0