单例模式要点:
使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
1.懒汉式
线程不安全实现
私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (uniqueInstance == null) ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 uniqueInstance = new Singleton(); 语句,这将导致多次实例化 uniqueInstance
public class Singleton {
private Singleton() {
}
private static Singleton uniqueInstance ;
public static Singleton getUniqueInstance (){
if (uniqueInstance == null) {
//线程不安全
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
懒汉式,线程安全实现
public class Singleton {
private Singleton() {
}
private volatile static Singleton uniqueInstance ;
//有性能损耗,每次都得获取锁,才能获取实例
public static synchronized Singleton getUniqueInstance (){
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
public class Singleton {
private Singleton() {
}
private volatile static Singleton uniqueInstance ;
public static Singleton getUniqueInstance (){
if (uniqueInstance == null) {
synchronized(Singleton.class){
if (uniqueInstance == null) { //双重检查
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
uniqueInstance 采用 volatile 关键字修饰也是很有必要的。uniqueInstance = new Singleton(); 这段代码其实是分为三步执行。 1分配内存空间 2初始化对象 3将 uniqueInstance 指向分配的内存地址, 但是由于 JVM 具有指令重排的特性,有可能执行顺序变为了 1>3>2,这在单线程情况下自然是没有问题。但如果是多线程下,有可能获得是一个还没有被初始化的实例,以致于程序出错。 使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行
2.饿汉式
线程不安全问题主要是由于 uniqueInstance 被多次实例化,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。 但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处
public class Singleton {
private Singleton() {
}
private static Singleton uniqueInstance =new Singleton();;
public static Singleton getUniqueInstance (){
return uniqueInstance;
}
}
3.静态内部类实现
当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。 这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
4.枚举实现
这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次
public enum Singleton {
uniqueInstance;
}