单例模式
有一些对象其实我们只需要一个就够了,比如线程池、缓存、对话框、处理偏好设置、注册表的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象。如果制造出多个,反而会导致许多问题。
单例模式定义
确保一个类只有一个实例,并提供一个全局访问点。
经典的单例模式实现
第一步:利用一个静态变量来记录Singleton类的唯一实例。
第二步:把构造器声明为私有的,只有在Singleton类内才可以调用构造器。
第三步:用getInstance()方法返回这个实例。
public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } //其他方法
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
如果多个线程来访问此单例就会有很大的麻烦,有可能在多个线程访问上述单例时会重复创建单例。我们只要把getInstance()变成同步(synchronized)方法,多线程灾难几乎就可以轻易地解决了。通过增加synchronized关键字到getInstance()方法,我们就可以迫使每个线程进入这个方法之前,要先等候别的线程离开该方法,即不会有两个线程同时进入这个方法,如:
public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
同步getInstance方法既简单又有效,但是同步一个方法可能会造成程序执行效率下降100倍。我们可以用“双重检查加锁”,在getInstance()中减少使用同步来解决。首先检查是否实例已经创建了,如果实例未创建,才进行同步。这样一来,只有第一次会同步:
public class Singleton { private volatile static Singleton instance; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } //其他方法
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
通过这样做后就可以大大减少getInstance的时间耗费。
说明:volatile修饰记录实例的静态变量instance,可以确保instance被初始化成Singleton实例时,多个线程正确地处理instance变量。
在声明静态变量时就初始化
如果应用程序总是创建并使用单例,或者在创建和运行时方面的负担不太繁重,可以用下面这种方式:
public class Singleton { private static Singleton instance = new Singleton(); //在静态初始化器中创建单例,这段代码保证了线程安全 private Singleton() { } public static Singleton getInstance() { return instance; } //其他方法
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单例,JVM保证在任何线程访问instance静态变量之前,一定先创建此实例。
注意:
每个类加载器都定义了一个命名空间,如果有两个以上的类加载器,不同的类加载器可能会加载同一个类,从整个程序来看,同一个类会被加载多次。如果这样的事情发生在单例上,就会产生多个单例并存的怪异现象。所以,如果你的程序有多个类加载器又同时使用了单例模式,请小心,解决办法就是自行指定类加载器,并指定同一个类加载器。
关于继承单例类的问题
继承单例类会遇到一个问题,就是构造器是私有的。你不能用私有构造器来扩展类。所以你必须把单例类的构造器改成公开的或受保护的。但是这样一来就不是真正的单例了,因为别的类可以实例化它。
还有另一个问题,单例的实现是利用静态变量,直接继承会导致所有的派生类共享同一个实例变量,这可能不是你想要的。
所以我不建议你这么做。
文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/weixin_40763897/article/details/88606183
- 点赞
- 收藏
- 关注作者
评论(0)