设计模式——单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

单例模式在实际的开发过程中,使用最常见。针对频繁使用的类,我们可以将它定义为一个单例来避免类对象频繁的创建销毁,提高效率。单例模式有以下特点:

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的唯一实例
  • 单例类给其它对象提供这一实例

单例模式的UML图 single

单例模式的实现方式 围绕单例模式的特点,一般常见的实现方式有以下几种:

  • 懒汉式
  • 饿汉式
  • Double Check Lock(DCL)双重检查锁定
  • 静态内部类实现
  • 枚举单例
  • 记录式单例

懒汉式 懒汉式是先声明一个静态类对象,然后在用户第一次调用初始化方法的时候进行初始化。

懒汉式线程非安全 这种模式的缺陷就是无法在多线程条件下保证实例只有一个。所以严格意义上来说,在多线程环境下就不能称之为单例模式了。

/***
 * 懒汉式
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance;
	private Singleton(){};
	
	public static Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

懒汉式线程安全 通过对getInstance方法添加同步方法锁的形式来达到线程同步,每次调用getInstance方法都会进行同步,这样在单线程环境下效率很低。

/***
 * 懒汉式线程安全
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance;
	private Singleton(){};
	
	public static synchronized Singleton getInstance(){
		if(instance == null){
			instance = new Singleton();
		}
		return instance;
	}
}

饿汉式 在单例对象声明的时候即进行实例化。

/***
 * 饿汉式
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private static Singleton instance = new Singleton();
	private Singleton(){};
	
	public static synchronized Singleton getInstance(){
		return instance;
	}
}

这种方式避免了在多线程环境下执行getInstance的问题,但是在不同的类加载器的环境下也可能出现不同的实例。

DCL模式(双重检查锁定) 双重检查锁定是通过多次判断instance是否为null来实现,然后结合volatile关键字来实现。

/***
 * DCL模式
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private volatile static Singleton instance;
	private Singleton(){};
	
	public static synchronized Singleton getInstance(){
		if(instance == null){
			synchronized (Singleton.class) {
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

双重检查锁定模式实现的亮点是在getInstance方法的实现以及实例的定义上。它通过两次判断null值进行实现。在第一次判断主要是为了不必要的锁机制,第二次判断是为了确保在null的情况下进行创建实例。这里使用volatile关键字来定义instance就是确保能够保持一致性。DCL模式资源利用率高,只有在第一次调用getInstance时才会继续拧初始化操作。

静态内部类

/***
 * 静态内部类模式
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private Singleton(){};
	
	public static synchronized Singleton getInstance(){
		return SingleBuilder.instance;
	}
	
	private static class SingleBuilder{
		private static final Singleton instance = new Singleton();
	}
}

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,即使Singleton类被装载了,instance不一定被初始化,因为SingleBuilder类没有被主动使用,只有显示通过调用使用实例时,才会显示装载SingleBuilder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的,所非常推荐这种方式。

枚举单例 枚举的每个实例在Java堆中有且只有一个副本,这种特性让枚举很容易就实现了单例模式。

enum SingletonEnum{
	INSTANCE;
	
	
	public void drawCircle(){
		System.out.print("drawCircle");
	}
}

	public static void main(String[] args) {
		SingletonEnum.INSTANCE.drawCircle();
	}

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,非常推荐使用。枚举在Java中和普通类一样,可以拥有自己的字段和方法,最重要的是枚举的实例创建是线程安全的,任何情况下都是一个单例。

记录式单例 记录式单例就是通过自定义管理器然后记录管理实例的创建创建,以此来保证单例。

/***
 * 记录式单例
 * @author Iflytek_dsw
 *
 */
public class Singleton {
	private static Map<String,Object> keyMap = new HashMap<>();
	private Singleton(){};
	
	public static Singleton getInstance(String key){
		return (Singleton) keyMap.get(key);
	}
	
	public static void registerSingle(String clazzName,Object obj){
		if(keyMap.get(clazzName) == null){
			keyMap.put(clazzName, obj);
		}
	}
}

记录式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。

总结 不管以哪种方式实现单例模式,核心都是私有化构造函数,并且通过一个静态方法获取单个实例,这个过程中出现的变种就是要保证线程是否安全、放置序列化导致新生成对象的问题。选择哪种方式取决于使用环境:是否是多线程环境、性能要求、JDK版本等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值