一、应用场景
一般会在以下两种情况下采用单例模式:
- 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。
- 控制资源的情况下,方便资源之间的互相通信。
场景举例:
- 为了方便线程池对池中的线程进行管理,一般线程池也会采用单例模式。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
- 外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
- Windows的Task Manager(任务管理器)就是很典型的单例模式。
- Windows的Recycle Bin(回收站)也是典型的单例应用。
- 网站的计数器,一般也是采用单例模式实现,否则难以同步。
- Web应用的配置对象的读取,由于配置文件是共享的资源。
- 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
二、单例原则
- 构造方法私有化。
- 以静态方法或者枚举返回实例。
- 实例化变量引用私有化。
- 确保反序列转换时不会重新构建对象。
三、怎么写单例模式
(一)饿汉模式
饿汉模式在类被初始化时就已经在内存中创建了对象,以空间换时间,因此不存在线程安全问题。
package com.fuping.liuqu.demo.design.pattern.singleton;
/**
* 单例模式,饿汉模式
*/
public class Singleton1 {
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
private static final Singleton1 INSTANCE = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return INSTANCE;
}
}
(二)懒汉模式
懒汉模式在方法被调用后才创建对象,以时间换空间,在多线程环境下存在风险。
package com.fuping.liuqu.demo.design.pattern.singleton;
/**
* 单例模式,懒汉模式
*/
public class Singleton2 {
private String field;
private static Singleton2 instance = null;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
private Singleton2() {
}
public static Singleton2 getInstance() {
if (null == instance) {
instance = new Singleton2();
}
return instance;
}
}
(三)双重检入模式
必须给INSTANCE增加volatile描述,以解决指令重排序的问题。
package com.fuping.liuqu.demo.design.pattern.singleton;
/**
* 单例模式,双重检入模式
*/
public class Singleton3 {
private String field;
private volatile static Singleton3 INSTANCE = null;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
private Singleton3() {
}
public static Singleton3 getInstance() {
if (null == INSTANCE) {
synchronized (Singleton3.class) {
if (null == INSTANCE) {
synchronized (Singleton3.class) {
INSTANCE = new Singleton3();
}
}
}
}
return INSTANCE;
}
}
(四)静态内部类模式
静态内部类的优势在于第一次加载Singleton4类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder类并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton4 类的唯一性。
package com.fuping.liuqu.demo.design.pattern.singleton;
/**
* 单例模式,静态内部类模式
*/
public class Singleton4 {
private Singleton4() {
}
public Singleton4 getInstance() {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton4 INSTANCE = new Singleton4();
}
}
(五)单元素枚举模式
优势:
- 可以避免反射攻击问题(构造方法私有化,并不能阻止类被反射)
- 可以解决序列化问题
package com.fuping.liuqu.demo.design.pattern.singleton;
/**
* 单例模式,枚举类模式
*/
public enum Singleton5 {
INSTANCE;
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public Singleton5 getInstance() {
return INSTANCE;
}
}