单例模式
单例设计模式
保证一个类只有一个实例
懒汉式 (在需要的时候才实例化)
在单线程环境下 实例只有一个
class SingletonTest{ public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); // 都是同一个实例 System.out.println(singleton1 == singleton2); } } public class Singleton { public static Singleton singleton; // 私有化构造方法 private Singleton(){}; // 调用该方法的时候才实例化 public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
在多线程的环境下 会创建多个实例
public static void main(String[] args) { new Thread(()->{ Singleton instance = Singleton.getInstance(); System.out.println(instance); }).start(); new Thread(()->{ Singleton instance = Singleton.getInstance(); System.out.println(instance); }).start(); }
// 加锁 public synchronized static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; }
因为在多线程 并发访问 if(singleton == null) 的时候 会造成线程不安全问题 导致创建2个不同的实例
解决方法:
如果加 synchronized 程序会有性能的损耗 每次有线程访问 getInstance 方法的时候 都需要进行加锁操作
优化(双重锁):
public static Singleton getInstance(){ // 如果singleton等于空的时候 是不需要加锁的 if(singleton == null){ synchronized (Singleton.class){ singleton = new Singleton(); } } return singleton; }
还是会创建两个不同的实例 假如说第一个线程返回实例 第二个线程排队等待 等第一个线程释放锁的时候 第二个线程还是会创建一个新的对象
// 防止 使用到未初始化的实例和指令重排 public volatile static Singleton singleton; // 私有化构造方法 private Singleton(){}; // 调用该方法的时候才实例化 public static Singleton getInstance(){ // 如果singleton等于空的时候 是不需要加锁的 if(singleton == null){ synchronized (Singleton.class){ // 多加一次判断 if(singleton == null){ singleton = new Singleton(); } } } return singleton; }
只实例化了一个对象
静态内部类实现单例
class InnerSingletonTest{ public static void main(String[] args) { InnerSingleton instance1 = InnerSingleton.getInstance(); InnerSingleton instance2 = InnerSingleton.getInstance(); System.out.println(instance1 == instance2); new Thread(()->{ InnerSingleton instance = InnerSingleton.getInstance(); System.out.println(instance); }).start(); new Thread(()->{ InnerSingleton instance = InnerSingleton.getInstance(); System.out.println(instance); }).start(); } } public class InnerSingleton { // 需要的时候 才实例话 private static class InnerSingletonOne{ private static InnerSingleton innerSingleton = new InnerSingleton(); } private InnerSingleton(){ } public static InnerSingleton getInstance(){ return InnerSingletonOne.innerSingleton; } }
只有一个实例
饿汉式
class HungryTest{ public static void main(String[] args) { } } public class Hungry { // 类初始化的时候就创建了 private static Hungry hungry = new Hungry(); private Hungry(){}; public Hungry getHungry(){ return hungry; }
通过反射多例化
class InnerSingletonTest{ public static void main(String[] args) throws Exception{ InnerSingleton instance1 = InnerSingleton.getInstance(); Constructor<InnerSingleton> declaredConstructor = InnerSingleton.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); InnerSingleton innerSingleton = declaredConstructor.newInstance(); System.out.println(innerSingleton == instance1); } } public class InnerSingleton { // 需要的时候 才实例话 private static class InnerSingletonOne{ private static InnerSingleton innerSingleton = new InnerSingleton(); } private InnerSingleton(){ } public static InnerSingleton getInstance(){ return InnerSingletonOne.innerSingleton; } }
通过反射可以创建多个实例
解决方法
public class InnerSingleton { // 需要的时候 才实例话 private static class InnerSingletonOne{ private static InnerSingleton innerSingleton = new InnerSingleton(); } private InnerSingleton(){ if(InnerSingleton.getInstance() != null){ throw new RuntimeException("不允许创建多个实例"); } } public static InnerSingleton getInstance(){ return InnerSingletonOne.innerSingleton; } }
防止创建多个实例
使用枚举
class SingletonEnumTest{ public static void main(String[] args) { SingletonEnum singletonEnum1 = SingletonEnum.INSTANCE; SingletonEnum singletonEnum2= SingletonEnum.INSTANCE; System.out.println(singletonEnum1 == singletonEnum2); } } public enum SingletonEnum { INSTANCE; }
反射不支持enum对象的创建
class SingletonEnumTest{ public static void main(String[] args) throws Exception { SingletonEnum singletonEnum1 = SingletonEnum.INSTANCE; Constructor<SingletonEnum> instance = SingletonEnum.class.getDeclaredConstructor(String.class,int.class); instance.setAccessible(true); SingletonEnum singletonEnum = instance.newInstance("INSTANCE",0); } } public enum SingletonEnum { INSTANCE; }
反序列化对单例造成的破坏
public class InnerSingleton implements Serializable { // 需要的时候 才实例话 private static class InnerSingletonOne{ private static InnerSingleton innerSingleton = new InnerSingleton(); } private InnerSingleton(){ if(InnerSingleton.getInstance() != null){ throw new RuntimeException("不允许创建多个实例"); } } public static InnerSingleton getInstance(){ return InnerSingletonOne.innerSingleton; } }
public static void main(String[] args) throws Exception{ // 将对象序列化 存储到磁盘 InnerSingleton instance1 = InnerSingleton.getInstance(); ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream("test")); objectOutputStream.writeObject(instance1);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test")); InnerSingleton instance2 = (InnerSingleton)objectInputStream.readObject(); System.out.println(instance1 == instance2); }
序列化回来 不是同一个对象了
解决方法
重写readResolve() 方法 记得加版本id
public class InnerSingleton implements Serializable { // 需要的时候 才实例话 private static class InnerSingletonOne{ private static InnerSingleton innerSingleton = new InnerSingleton(); } private InnerSingleton(){ if(InnerSingleton.getInstance() != null){ throw new RuntimeException("不允许创建多个实例"); } } public static InnerSingleton getInstance(){ return InnerSingletonOne.innerSingleton; } Object readResolve() throws ObjectStreamException { return InnerSingletonOne.innerSingleton; } }
枚举类型支持反序列化机制
序列化枚举类 然后读回来
public static void main(String[] args) throws Exception { SingletonEnum singletonEnum1 = SingletonEnum.INSTANCE; ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream("test1")); objectOutputStream.writeObject(singletonEnum1); objectOutputStream.close(); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test1")); SingletonEnum singletonEnum2 = (SingletonEnum)objectInputStream.readObject(); System.out.println(singletonEnum1 == singletonEnum2); }
还是原来的对象