单例模式
- 意图: 保证一个类只有一个实例,并提供一个访问它的全局访问点
- 好处: 避免一个全局使用的类频繁地创建和销毁
- 应用: Spring 中的 bean 默认都是单例模式
实现
饿汉型
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
-
验证
public class SingletonPatternDemo implements Runnable { @Override public void run() { System.out.println("INFO::Singleton.hashcode -> " + Singleton.getInstance().hashCode()); } public static void main(String[] args) { SingletonPatternDemo demo = new SingletonPatternDemo(); Thread t1 = new Thread(demo); Thread t2 = new Thread(demo); Thread t3 = new Thread(demo); t1.start(); t2.start(); t3.start(); } }
-
饿汉型单例模式实现简单,但实例对象在被调用前就已经创建完毕了,容易产生垃圾对象造成内存浪费。
懒汉型
-
DCL(Double-Checked Locking)
public class Singleton { private volatile static Singleton singleton; private Singleton() {} public static Singleton getInstance() { if(singleton == null) { synchronized (Singleton.class) { if(singleton == null) { singleton = new Singleton(); } } } return singleton; } }
-
静态内部类
public class Singleton { private static class SingletonHandler { private static Singleton singleton = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return SingletonHandler.singleton; } }
-
验证
- 比较两种懒汉型的单例模式,采用静态内部类的单例模式没有锁,在高并发环境下性能更好;而 DCL 方法中采用了 synchronized 保证线程安全,又做了双重检查,所以性能远不如静态内部类创建的单例模式。
- DCL 下单例对象采用了 volatile 修饰,这是为了避免 JVM 进行指令的重排序。原本在新建一个对象时,要经过:①为对象分配内存空间;②对象初始化;③将引用指向对象的内存空间地址。但 JVM 可能将指令重排序为 ①③②,如果A线程按照这个顺序执行到③时,B线程尝试获取单例对象,此时栈中已有单例对象的引用,因此B线程可以获取到,但此时单例对象仍未进行初始化,可能导致程序出错。