常见的单例设计模式,有如下五种写法,在编写单例代码的时候要注意以下几点:
1、构造器需要私有化
private static Logger instance = new Logger();
private FileWriter writer;
private Logger() {
File file = new File(basePath);
try {
writer = new FileWriter(file, true); //true表示追加写入
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Logger getInstance(){
return instance;
}
public void log(String message) {
try {
writer.write(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void setBasePath(String basePath) {
this.basePath = basePath;
}
}
2、暴露一个公共的获取单例对象的接口
3、是否支持懒加载(延迟加载)
4、是否线程安全
1、饿汉式
饿汉式的实现方式比较简单。在类加载的时候,instance 静态实例就已经创建并初
始化好了,所以,instance 实例的创建过程是线程安全的。从名字中我们也可以看
出这一点。具体的代码实现如下所示:
public class EagerSingleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
事实上,恶汉式的写法在工作中反而应该被提倡,面试中不问,只是应为他简单。很
多人觉得饿汉式不能支持懒加载,即使不使用也会浪费资源,一方面是内存资源,一
方面会增加初始化的开销。
1、现代计算机不缺这一个对象的内存。
2、如果一个实例初始化的过程复杂那更加应该放在启动时处理,避免卡顿或者构造
问题发生在运行时。满足 fail-fast 的设计原则。
2、懒汉式
有饿汉式,对应地,就有懒汉式。懒汉式相对于饿汉式的优势是支持延迟加载,具体
的代码实现如下所示:
public class LazySingleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
以上的写法本质上是有问题,当面对大量并发请求时,其实是无法保证其单例的特点
的,很有可能会有超过一个线程同时执行了new Singleton();
当然解决他的方案也很简单,加锁呗:
public class Singleton {
private static Singleton instance;
private Singleton (){}
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
以上的写法确实可以保证jvm中有且仅有一个单例实例存在,但是方法上加锁会极大
的降低获取单例对象的并发度。同一时间只有一个线程可以获取单例对象,为了解决
以上的方案则有了第三种写法。
3、双重检查锁
饿汉式不支持延迟加载,懒汉式有性能问题,不支持高并发。那我们再来看一种既支
持延迟加载、又支持高并发的单例实现方式,也就是双重检测实现方式:
在这种实现方式中,只要 instance 被创建之后,即便再调用 getInstance() 函数也不
会再进入到加锁逻辑中了。所以,这种实现方式解决了懒汉式并发度低的问题。具体
的代码实现如下所示:
public class DclSingleton {
// volatile如果不加可能会出现半初始化的对象
// 现在用的高版本的 Java 已经在 JDK 内部实现中解决了这个问题(解决的方法很
简单,只要把对象 new 操作和初始化操作设计为原子操作,就自然能禁止重排序),
为了兼容性我们加上
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}