单例模式是为了保证一个类在一个系统中同时只有一个实例存在(可以很好地节约资源,避免频繁创建和销毁对象)。
比如spring中的@Autowired。
实现的基本原理:每次获取对象前,先判断系统中是否已经有这个单例对象,有则返回,没有则创建。单例模式的类构造函数时私有的,不允许类外方法使用new关键字创建对象。
懒汉模式(线程安全)
在类中定义单例对象,但并未实例化,是在获取单例对象的方法中进行实例化的。所以在第一次调用懒汉模式时,该对象一定为空,然后实例化对象并赋值,这样下次就能直接获取对象了。
public class Main {
public static void main(String[] args) {
LazySingleton singleton = LazySingleton.getInstance();
System.out.println(singleton.hashCode());
LazySingleton singleton1 = LazySingleton.getInstance();
System.out.println(singleton1.hashCode());
}
}
class LazySingleton{
private static LazySingleton instance;
private LazySingleton(){}
public static synchronized LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
}
运行结果
饿汉模式
饿汉模式是在定义单例对象的同时进行初始化(在类加载完成时,该类实例已经存在在JVM中)
public class Main {
public static void main(String[] args) {
HungrySingleton hungrySingleton1 = HungrySingleton.getInstace();
System.out.println(hungrySingleton1.hashCode());
HungrySingleton hungrySingleton2 = HungrySingleton.getInstace();
System.out.println(hungrySingleton2.hashCode());
}
}
class HungrySingleton{
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstace(){
return hungrySingleton;
}
}
静态内部类
在类中定义一个静态内部类,将对象实例的定义和初始化放在内部类中完成,在获取对象时要通过静态内部类调用其单例对象。
因为类的静态内部类在JVM中是唯一的,可以很好地保障单例对象的唯一性。
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
双重校验锁
在懒汉模式的基础上做进一步优化。
在之前的懒汉式synchronized方法,会导致很大的性能开销,并且加锁只需要在第一次初始化的时候使用,之后的调用没必要加锁了。
所以使用双重校验锁(double checked locking)进行优化。先判断对象是否被初始化,若未初始化,则进行加锁,再判断对象是否初始化,若未初始化,则进行初始化。
- 使用volatile关键字修饰singleton对象,因为volatile会禁止指令重排,所有写操作都将发生在读操作之前。
new Object()在底层可以分解成三个步骤:
1.分配内存空间
2.初始化对象
3.将对象指向刚分配的内存空间
如果对象不使用volatile修饰,假如步骤2.3及进行重排序,那么可能会访问到一个初始化未完成的对象。
比如:
public class Main {
public static void main(String[] args) {
DCLSingleton dclSingleton = DCLSingleton.getInstance();
System.out.println(dclSingleton.hashCode());
DCLSingleton dclSingleton1 = DCLSingleton.getInstance();
System.out.println(dclSingleton1.hashCode());
}
}
class DCLSingleton{
private volatile static DCLSingleton dclSingleton;
private DCLSingleton(){}
public static DCLSingleton getInstance(){
if(dclSingleton == null){
synchronized (DCLSingleton.class){
if(dclSingleton == null){
dclSingleton = new DCLSingleton();
}
}
}
return dclSingleton;
}
}