- 单件模式
定义
单件模式确保一个类只有一个实例,并提供一个全局访问点。
优缺点
优点:全局变量需要在程序启动时创建好对象(有的JVM的实现是:在用到的时候创建对象),消耗不必要的资源,而单件模式可以在我们需要时才创建对象,和全局变量一样方便,又没有全局变量的缺点。
应用场景
有一些对象我们其实只需要一个,比方说:线程池(threadpool)、缓存(cache)、对话框、偏好设置和注册表(registry)的对象、日志对象、打印机的驱动程序的对象。
例子
经典的单例模式的实现(如果我们不需要这个实例,它就永远不会产生,这就是“延迟实例化”)
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if(null == uniqueInstance) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
处理多线程(在多线程情况下,有可能出现创建多个实例的 情况),下面是一种解决方法:
public class Singleton {
private static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(null == uniqueInstance) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
但是这样做的缺点很明显:降低性能。而且只有第一次执行此方法时,才需要同步。一旦创建好uniqueInstance后,就不需要同步这个方法了。
下面是一些选择:
1.如果getInstance()的性能对应用程序不是很关键,就什么也别做。
2.使用“急切”(eagerly)创建实例,而不用延迟实例的做法。(JVM在加载这个类时,马上创建实例)
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return uniqueInstance;
}
}
3.用双重加锁,用双重检查加锁(double-checked locking),首先检查实例是否创建,如果尚未创建,“才”进行同步。这样一来,只有第一次会同步。(volatile支持jdk1.4以上版本)
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(null == uniqueInstance) {
synchronized (Singleton.class) {
if(null == uniqueInstance) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
设计原则
封装变化
多用组合,少用继承
针对接口编程,不针对实现编程
为交互对象之间的松耦合设计而努力
类应该对扩展开放,对修改关闭
依赖抽象,不要依赖具体
小结
对多线程使用单件模式要尤其注意,加锁或“急切”创造实例的方法视情况选择。
小知识
volatile关键字:
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
2)禁止进行指令重排序。