**
一、什么是单例模式:
**
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见场景:
1.Windows的任务管理器
2.Windows的回收站
3.项目中,读取配置文件的类
4.网站的计数器,用来保持同步
5.数据库连接池的设计
6.在Servlet编程中,每个Servlet也是单例
7.在Spring中,每个Bean默认就是单例的
**
二、单例模式的优点:
**
1.由于单例模式只生成一个实例,减少了系统性能的开销
2.单例模式可以在系统设置全局的访问点,优化共享资源的访问
**
三、常见的五种单例模式以及实现方式:
**
1.饿汉式(线程安全,调用效率高,不能延时加载):
单例这个类的对象中不需要开辟内存空间可以使用。
/**
* 饿汉式单例模式
*
* @author donghaoWang
*
*/
public class SingletonDemo1 {
//1.构造器私有化
private SingletonDemo1() {
}
//2.类初始化的时候,立即加载该对象
private static SingletonDemo1 instance=new SingletonDemo1();
//3.提供获取该对象的方法,没有synchronized,效率高!
public static SingletonDemo1 getInstance() {
return instance;
}
}
由于饿汉式的单例模式是随着类的初始化生成实例,如果长时间不调用getInstance()会造成大量内存控件的浪费。继而引出懒汉式
2.懒汉式(线程安全,调用效率不高,可以延时加载):
如果单例这个类的对象需要开辟很多内存空间,可以使用。
/**
* 懒汉式单例模式
*
* @author donghaoWang
*
*/
public class SingletonDemo2 {
//1.私有化构造器
private SingletonDemo2() {
}
//2.类初始化的时候,不立即加载该对象
private static SingletonDemo2 instance;
//3.提供获取该对象的方法,为防止有多个线程同时进来,造成线程不安全,加上synchronized,效率较低!
public static synchronized SingletonDemo2 getInstance() {
if(instance ==null) {
instance=new SingletonDemo2();
}
return instance;
}
}
由于在getInstance()方法是同步方法,为了提高方法效率继而有DCL懒汉式
3.DCL懒汉式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用):
重点优化额懒汉式同步慢的问题,变成同步块,添加volatile关键字保证其原子性。
/**
* DCL懒汉式单例(Double-Checked Locking)
*
* @author donghaoWang
*
*/
public class SingletonDemo3 {
//1.私有化构造器
private SingletonDemo3() {
}
//2.类初始化的时候,不立即加载该对象
private volatile static SingletonDemo3 instance;
//3.提供获取该对象的方法,为了进一步提高效率增加了同步锁
public static SingletonDemo3 getInstance() {
if(instance==null) {
synchronized(SingletonDemo3.class) {
if(instance==null) {
instance=new SingletonDemo3();
}
}
}
return instance;
}
}
4.饿汉式改进:静态内部类式(线程安全,调用效率高,可以延时加载):
/**
*饿汉式改进(静态内部类式)单例模式
*
* @author donghaoWang
*
*/
public class SingletonDemo4 {
private SingletonDemo4() {}
private static class InnerClass{
private static final SingletonDemo4 instance=new SingletonDemo4();
}
public static SingletonDemo4 getInstance() {
return InnerClass.instance;
}
}
通过内部类的去实例化对象,这样只要不调用getInstance()方法内部类就不会实例化对象,并且添加了final关键字保证了线程的安全。但是这种单例模式是可以被反射机制给破坏的。例如:
public static void main(String[] args) throws Exception {
SingletonDemo4 instance =SingletonDemo4.getInstance();
Constructor<SingletonDemo4> declaredConstructor=
SingletonDemo4.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SingletonDemo4 instance2 =declaredConstructor.newInstance();
System.out.println(instance==instance2);
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
}
我们可以看到结果,我们的单例被java的反射机制给破坏。
为了保证绝对的线程安全,而且我们可以在反射看源码中看到,枚举类型会天然的抛出异常
5.枚举单例(线程安全,调用效率高,不能延时加载):
public enum SingletonDemo5 {
INSTANCE;
public SingletonDemo5 getInstance() {
return INSTANCE;
}
}
枚举是目前最推荐的单例模式的写法,因为其足够简单,也不需要去自己保证它线程的安全,同时还可以有效的保证反射来破坏单例。