单例的目的是为了保证运行时Singleton类只有唯一的一个实例,最常用的地方比如拿到数据库的连接,Spring的中创建BeanFactory这些开销比较大的操作,而这些操作都是调用他们的方法来执行某个特定的动作。主要有以下两种实现方式:
饥饿模式:
public class Singleton {
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
/*这会带来潜在的性能问题:如果这个对象很大,在没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。但该模式能保证多线程时的线程安全*/
懒汉模式:
public class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
/*这也会有问题,如果是多线程的情况下,如果两个线程同时执行到了if(instance == null)就都会创建一个对象
所以懒汉式只适用于单线程的情况*/
如果多线程情况下,我们又不想用饿汉式,该怎么办呢?我们可以使用synchronized 来同步,并使用双重检查锁和volatile保证可能出现的问题:
双重检查锁定具体分析见:https://blog.csdn.net/zcl_love_wx/article/details/80758162
public class Singleton{
// 静态属性,volatile保证可见性和禁止指令重排序
private volatile static Singleton instance = null;
// 私有化构造器
private Singleton(){}
public static Singleton getInstance(){
// 第一重检查锁定
if(instance==null){
// 同步锁定代码块
synchronized(Singleton.class){
// 第二重检查锁定
if(instance==null){
// 注意:非原子操作
instance=new Singleton();
}
}
}
return instance;
}
}
上面的synchronized 同步性能不够好。可以改为如下的方式:
目前最优的方式:静态内部类
public class Resource{
private Resource(){}
private static class ResourceHolder {
// 静态内部类里其实也是饿汉模式
public static Resource resource = new Resource();
}
public static Resource getResource() {
return ResourceHolder.resource;
}
}