标签:处理 方法 情况 获取对象 利用 ack ble 内存模型 空间
概述:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
关键点:
饿汉式单例模式(在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快)
public class EagerSingle { //饿汉模式单例 //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快 private static EagerSingle single = new EagerSingle();//静态私有成员,已初始化 private EagerSingle() { //私有构造函数 } public static EagerSingle getInstance() {//静态,不用同步(类加载时已初始化,不会有多线程的问题) return single; } }
懒汉模式声明一个静态对象,并且在用户第一次调用getInstance时进行初始化。
public class EagerSingleton { //饿汉模式单例 //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快 private static EagerSingleton instance = new EagerSingleton();//静态私有成员,已初始化 private EagerSingleton() { //私有构造函数 } public static EagerSingleton getInstance() {//静态,不用同步(类加载时已初始化,不会有多线程的问题) return instance; } }
synchronized关键字保证了同步,在多线程情况下单例的唯一性。
存在问题:即使instance已经存在,每次调用getInstance依然会进行同步,这样就会消耗不必要的资源。
总结:懒汉模式的优点是只有在使用时才会实例化单例对象,在一定程度上节约了资源;缺点是第一次加载时需要进行实例化,反应稍慢;最大问题是每次调用都会进行同步吗,造成不必要的同步开销。这种模式一般不建议使用。
DCL方式实现单例的优点是既能在需要时才初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。
public class DCLSingleton { //Double Check Lock单例模式 //懒汉模式的改进 //但仍然存在隐患 private static DCLSingleton instance = null; private DCLSingleton() { } public static DCLSingleton getInstance() { if (instance == null) {//第一层判断主要是为了避免不必要的同步 synchronized (DCLSingleton.class) { if (instance == null) {//第二层判空是为了在null情况下创建实例 instance = new DCLSingleton(); } } } return instance; } }
亮点在getInstance方法上,有两次判空。第一层判断主要是为了避免不必要的同步,第二层判空是为了在null情况下创建实例。
single = new Singleton();
实际上并不是一个原子操作,这句代码实际做了3件事
问题:但由于java编译器允许处理器乱序执行,上述顺序2、3是不能保证的,可能是1-2-3也可能是1-3-2;如果是后者,3执行了已经非空,再走2会出现问题,这就是DCL失效。
解决: volatile关键字
// private static DCLSingleton instance = null; private volatile static DCLSingleton instance = null;
只需要加上volatile关键字,如上述代码操作就可以保证instance对象每次都是从主内存中读取的,就可以采用DCL来完成单例模式了。当然,volatile或多或少会影响到性能,但考虑到程序的正确性,牺牲点性能还是值得的。
总结:
public class InnerSingleton { private InnerSingleton() { } public static InnerSingleton getInstance() { return InnerSingletonHolder.instance; } /** * 静态内部类 */ private static class InnerSingletonHolder { private static final InnerSingleton instance = new InnerSingleton(); } }
总结:第一次加载InnerSingleton类时并不会初始化instance,只有在第一次调用InnerSingleton的getInstance方法时才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载InnerSingleton类,这种方法不仅能保证线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化,所以这也是一种推荐的单利模式实现方法
public enum EnumSingleton { INSTANCE; public void doSomething() { //do sth ... } }
震惊?没错!就是枚举!
补充: 通过序列化可以将一个单例的实例对象写到磁盘,然后再读回来,从而有效的获取一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。
标签:处理 方法 情况 获取对象 利用 ack ble 内存模型 空间
原文地址:https://www.cnblogs.com/ivoo/p/10725893.html