设计模式之单例模式(饿汉模式,懒汉模式,双重检查加锁模式)
单例模式,顾名思义只有一个单例,应用场景还是很普遍的,比如网站中的人数计数器、连接数据库中的连接实例等等。
单例模式:确保一个类只有一个实例,并提供一个全局访问点。到底怎么回事?即把某个类设计成自己管理的一个单独实例,同
时避免其他类再自行产生实例。要想取得实例,通过单例类是唯一的途径。也提供对这个实例的全局访问点:当你需要实例时,向
类查询,它会返回单个单例。
实现:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
这种模式又称为懒汉模式(懒加载模式),是最基本的实现方式,最大的缺点是非线程安全的,一旦是多线程情况下,它就不能
正常使用了。延迟加载模式还有另一个特点,就是在使用到的时候才去加载,如果实例化的对象过程比较复杂,可能会出现卡顿
的情况,且当多线程情况下,可能会出现行为异常,那么如何解决线程安全问题呢?利用同步锁可以解决线程安全问题,实现如下:
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public synchronized static Singleton getSingleton() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
利用同步锁关键字synchronized,这样一来,在多线程情况下,访问获取单例就需要进行同步等待,达到线程安全问题。
而此时又会出现另一个问题,那就是性能问题,同步的时候线程访问方法出现等待的过程,性能大大降低,拖垮性能。那怎么
办?以下有一些方式可以改善问题。
1、如果getSingleton()的性能对应用程序不是很关键,或者是影响不大,可以什么都不做- -。。。
2、使用急切的方式(饿汉式)来初始化实例
实现如下:
public class Singleton {
//饿汉式
private static Singleton singleton = new Singleton();
private Singleton() {
}
public static Singleton getSingleton() {
return singleton;
}
}
这种方式依赖JVM加载这个类时马上创建唯一的单例,缺点就是浪费内存,如果用不到的话。
3、双重检查加锁模式
public class Singleton {
//双重检查加锁
private volatile static Singleton singleton;
private Singleton() {
}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
用双重检查加锁,在getSingleton()中减少使用同步,首先检查是否实例已经创建了,如果尚未创建,“才”进行同步。这样一
来,只有第一次会同步,这正是我们想要的。
volatile关键词确保,当singleton变量被初始化成Singleton实例时,多个线程正确地处理singleton变量。
总结:
1、单例模式确保程序中一个类只有一个实例
2、单例模式也提供访问这个实例的全局点
3、在java中实现单例模式,需要一个静态变量,私有的静态构造器和一个公共的获取实例的方法
4、确定在性能和资源上的限制,然后小心地选择适合的方案来实现单例,以解决多线程的问题
5、如果你的类有多个类加载器,可能导致单例失效,而产生多个单例,可以选定类加载器来解决此问题
以上是对单例模式的一些理解。