单例模式是一种创建型设计模式、当我们写程序时遇到某个类在全局应用中需要经常被用到且这个类比较大,我们就可以考虑使用单例模式进行设计,减轻GC压力提高响应速度。比如线程池、数据库连接池等都可以使用单例进行设计。比如Spring的IOC也用到了单例模式。
单例模式的设计主要有这么几个重点
- 构造方法不对外开放,一般为private
- 通过静态方法返回实例
- 确保类的实例有且仅有一个,特别是在多线程的环境下
- 尽量确保线程安全
饿汉式
理解static关键字和JVM的加载顺序对理解饿汉式不难、static修饰的部分随着类加载的时候而一起被加载到内存中去了。所以饿汉的缺点也是这个饥不择食、但万一设计的这个单例对若干个模块很重要但是使用者根本没用到这些模块的功能呢?就造成了资源浪费问题。/**
* 饿汉饿汉,太饿了所以一开始就创建好实例初始化进内存了
* 利用类加载机制避开了多线程安全问题
*/
public class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton(){}
private static Singleton getInstance(){
return singleton;
}
}
懒汉式
懒汉解决了饿汉初始化加载进内存的问题,但为了保证多线程安全用了同步,但如果这个类用户经常会使用到、同步就会耗点时间,每次都要判断锁、获取锁、释放锁这几个过程。/**
* 懒汉懒汉太懒了,等到别人访问它它在创建对象
* 考虑多线程安全问题,使用synchronized同步方法解决
*/
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
private static synchronized Singleton getInstance(){
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
静态内部类
利用类的加载机制避开初始化实例对象和多线程安全问题,推荐使用。public class Singleton {
private static class Holder{
private static final Singleton instance = new Singleton();
}
private Singleton(){}
private static Singleton getInstance(){
return Holder.instance;
}
}
双重加锁DCL
双重加锁,既避免了初始化实例,又解决了每次访问的同步问题。第一层判空是为了避免不必要的同步,第二层判空是为了在single为null的情况下才创建实例。加上volatile关键字是为了防止指令重排序导致DCL失效、但《Java并发编程实践》并不提倡DC方式,推荐静态内部类的方式public class Singleton {
private Singleton() {}
private volatile static Singleton singleton = null;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}