一、模式定义
保证一个类只有一个实例,并且提供一个全局访问点
二、应用场景
重量级的对象,不需要多个实例,如线程池,数据库连接
三、饿汉模式
public class Singleton {
//类加载的时候instance就已经指向了一个实例
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
类加载的 初始化阶段就完成了 实例的初始化 。本质上就是借助于jvm 类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)
类加载过程:
1.加载:二进制数据到内存中, 生成对应的Class数据结构,
2.连接: a. 验证, b.准备(给类的静态成员变量赋默认值),c.解析
3.初始化: 给类的静态变量赋初值
四、懒汉式模式
public class SingletonDemo2 {
private static SingletonDemo2 instance = null;
private SingletonDemo2() {
}
//运行时加载对象
public static SingletonDemo2 getInstance() {
if (instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
由于该模式是在运行时加载对象的,所以加载类比较快,但是对象的获取速度相对较慢,且线程不安全
五、双层锁模式
public class LazySingleton{
private volatile static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance() {
if (instance==null){
synchronized (LazySingleton.class){
if (instance==null){
instance=new LazySingleton();
}
}
}
return instance;
}
}
延迟加载, 只有在真正使用的时候,才开始实例化。
volatile:防止指令重排
指令重排
我简单的讲一下,.java文件会被编译成.class文件,比如说我们的.java文件的代码执行顺序为A->B->C>D,但在实实际执行的时候JAVA虚拟机为了性能考虑,编译器和CPU可能会对指令重新排序。我们的代码执行顺序可能就会为A->C->B->D,这就是我理解的指令重排
六、枚举
public class Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
七、静态内部类
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonHoler.INSTANCE;
}
}
八、反射攻击
Constructor<LazySingleton> declaredConstructor=LazySingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible( true );
LazySingleton lazySingleton=declaredConstructor.newInstance();
System.out.println(lazySingleton==LazySingleton.getInstance());
九、序列化攻击
public class serializeSingletonTest {
public static void main(String[] args) { serializeDestroyMethod();}
private static void serializeDestroyMethod() {
HungrySingleton hungrySingleton=null;
HungrySingleton hungrySingletonNew=null;
hungrySingleton=HungrySingleton.getInstance();
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(hungrySingleton);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
hungrySingletonNew= (HungrySingleton) ois.readObject();
System.out.println(hungrySingleton==hungrySingletonNew);
}
}