1、什么是单例模式
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
2、单例模式的使用场景
在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。
ex:
每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。
ps:Print Spooler是打印后台处理服务,即管理所有本地和网络打印队列及控制所有打印工作。
3、单例模式的特点
单例类只能有一个实例。
单例类必须自己创建自己的唯一实例。
单例类必须给所有其他对象提供这一实例。
单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。
4、单例的四大原则
- 构造器私有化
- 以静态方法或者枚举返回实例
- 确保实例只有一个,尤其是多线程环境
- 确保反序列化时不会重新构建对象
5、实现单例模式的方式(5种实现形式)
http://www.manongjc.com/detail/22-qlvqgpheqoalneu.html
5.1 懒汉式
该方式是使用synchronized关键字进行加锁,保证了线程安全性。
优点:在第一次调用才初始化,避免了内存浪费。
缺点:对获取实例方法加锁,大大降低了并发效率。
由于加了锁,对性能影响较大,不推荐使用。
public class SingletonLazy {
/**
* 私有实例
*/
private static SingletonLazy instance;
/**
* 私有构造方法
*/
private SingletonLazy() {
}
/**
* 唯一公开获取实例的方法(静态工厂方法),该方法使用synchronized加锁,来保证线程安全性
*
* @return
*/
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
5.2 饿汉式
饿汉式是利用类加载机制来避免了多线程的同步问题,所以是线程安全的。
优点:未加锁,执行效率高。
缺点:类加载时就初始化实例,造成内存浪费。
如果对内存要求不高的情况,还是比较推荐使用这种方式。
public class SingletonEager {
/**
* 私有实例,静态变量会在类加载的时候初始化,是线程安全的
*/
private static final SingletonEager instance = new SingletonEager();
/**
* 私有构造方法
*/
private SingletonEager() {
}
/**
* 唯一公开获取实例的方法(静态工厂方法)
*
* @return
*/
public static SingletonEager getInstance() {
return instance;
}
}
5.3 双重校验锁
利用了volatile修饰符的线程可见性(被一个线程修改后,其他线程立即可见),即保证了懒加载,又保证了高性能,所以推荐使用。
不过在有些地方是不推荐使用的,原因是由于JVM底层模型原因,偶尔会出问题。
public class SingletonDCL {
/**
* 私有实例,volatile修饰的变量是具有可见性的(即被一个线程修改后,其他线程立即可见)
*/
private volatile static SingletonDCL instance;
/**
* 私有构造方法
*/
private SingletonDCL() {
}
/**
* 唯一公开获取实例的方法(静态工厂方法)
*
* @return
*/
public static SingletonDCL getInstance() {
if (instance == null) {
synchronized (SingletonDCL.class) {
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
5.4 静态内部类
该模式利用了静态内部类延迟初始化的特性,来达到与双重校验锁方式一样的功能。由于需要借助辅助类,并不常用。
public class SingletonInnerClass {
/**
* 私有构造方法
*/
private SingletonInnerClass() {
}
/**
* 唯一公开获取实例的方法(静态工厂方法)
*
* @return
*/
public static SingletonInnerClass getInstance() {
return LazyHolder.INSTANCE;
}
/**
* 私有静态内部类
*/
private static class LazyHolder {
private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
}
}
5.5 枚举类
该方式利用了枚举类的特性,不仅能避免线程同步问题,还防止反序列化重新创建新的对象。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式。
但由于这种编码方式还不能适应,所以实际工作中很少使用。
public enum SingletonEnum {
INSTANCE;
public void method() {
System.out.println("枚举类中定义方法!");
}
}