保证一个类只有一个实例,并提供一个访问该实例的全局节点。
如控制某些共享资源的访问权限,例如数据库或文件;
如为避免频繁创建一个可复用的对象,需要仅在首次时初始化;
巧妙利用Java类加载机制,保证多线程环境下的线程安全性。当一个类被加载时,其静态内部类是不会同时被加载的,只有第一次被调用时才会初始化,而且不能通过反射的方式获取内部的属性。
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonInstance.instance;
}
private static class SingletonInstance{
private static final Singleton instance = new Singleton();
}
}
在多线程环境下,为了提高实例初始化的性能,不是每次获取实例时在方法上加锁,而是当实例未创建时才会加锁。
这里使用volatile对instance进行修饰,是因为volatile禁止指令重排,保证了线程安全性。
创建一个对象的步骤非原子操作,大致可以分为3部分,
(1)给instance分配内存
(2)调用实例Singleton的构造函数初始化instance
(3)将instance指向分配的内存地址
在多线程情况下,假设A线程执行创建instance的操作,对于B线程有俩种状态,(1)(2)下为null, (3)下为非null;在创建对象不是一个原子操作的前提下,CPU为提高运行效率,可能会对3个操作进行指令重排,如果重排后指令执行顺序是1,3,2,那么B线程获取到instance状态。是未赋值下的状态,即可能是null
volatile禁止指令重排,通过添加读屏障和写屏障,保证在该变量不会把其后面的指令重排到内存屏障之前,不会把其前面的指令重排到内存屏障之后。
public class Singleton{
private volatile static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(this){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
类加载的时候就创建实例,使用私有构造函数实现全局单个实例的初始化,并使用public static final修饰,实现延迟加载和保证线程安全性。
public class Singleton{
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
它能够保证序列化和反序列化过程中实例的唯一性,而且不用担心线程安全问题。
public enum Singleton {
SERVICE_A {
@Override
protected void hello() {
System.out.println("hello, service A");
}
},
SERVICE_B {
@Override
protected void hello() {
System.out.println("hello, service B");
}
};
protected abstract void hello();
}