赞
踩
单例模式确保一个类只有一个实例,并提供一个全局访问点,实现单例模式的方法是私有化构造函数,通过getInstance()方法实例化对象,并返回这个实例
优点:能够节约当前堆内存,共享资源,全局使用,不需要频繁New对象,能够快速访问,提高性能。
缺点:当多个线程访问同一个单例对象的时候可能会存在线程安全问题。
分别是「饿汉」、「懒汉(非线程安全)」、「懒汉(线程安全)」、「双重校验锁」、「静态内部类」、「枚举」和「容器类管理」、「静态块初始化」
饿汉式
public class SingletonV1 { /** * 饿汉式 优点:先天性线程是安全的,当类初始化的 就会创建该对象 缺点:如果饿汉式使用过多,可能会影响项目启动的效率问题。 */ private static SingletonV1 singletonV1 = new SingletonV1(); /** * 将构造函数私有化 禁止初始化 */ private SingletonV1() { } public static SingletonV1 getInstance() { return singletonV1; } public static void main(String[] args) { SingletonV1 instance1 = SingletonV1.getInstance(); SingletonV1 instance2 = SingletonV1.getInstance(); System.out.println(instance1 == instance2); } }
懒汉式(线程不安全)
public class SingletonV2 { /** * 懒汉式 (线程不安全) */ private static SingletonV2 singletonV2; private SingletonV2() { } /** * 在真正需要创建对象的时候使用... * * @return */ public static SingletonV2 getInstance() { if (singletonV2 == null) { try { Thread.sleep(2000); } catch (Exception e) { } singletonV2 = new SingletonV2(); } return singletonV2; } public static void main(String[] args) { // 1.模拟线程不安全 for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { SingletonV2 instance1 = SingletonV2.getInstance(); System.out.println(Thread.currentThread().getName() + "," + instance1); } }).start(); } } }
懒汉式(线程安全)
public class SingletonV3 { /** * 懒汉式 线程安全 */ private static SingletonV3 singletonV3; private SingletonV3() { } /** * 能够解决线程安全问题,创建和获取实例时都上锁 ,效率非常低,所以推荐使用双重检验锁 * * @return */ public synchronized static SingletonV3 getInstance() { if (singletonV3 == null) { System.out.println("创建实例SingletonV3"); singletonV3 = new SingletonV3(); } System.out.println("获取SingletonV3实例"); return singletonV3; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { SingletonV3 instance1 = SingletonV3.getInstance(); System.out.println(Thread.currentThread().getName() + "," + instance1); } }).start(); } } }
双重检验锁(DCL)
public class SingletonV4 { /** * volatile 禁止重排序和 提高可见性 */ private volatile static SingletonV4 singletonV4; private SingletonV4() { } public static SingletonV4 getInstance() { if (singletonV4 == null) { // 第一次判断如果没有创建对象 开始上锁... synchronized (SingletonV4.class) { if (singletonV4 == null) { // 当用户抢到锁,判断初始化 System.out.println("第一次开始创建实例对象....获取锁啦..."); singletonV4 = new SingletonV4(); } } } return singletonV4; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { SingletonV4 instance1 = SingletonV4.getInstance(); System.out.println(Thread.currentThread().getName() + "," + instance1); } }).start(); } } }
静态内部类形式
public class SingletonV5 { private SingletonV5() { System.out.println("对象初始..."); } public static SingletonV5 getInstance() { return SingletonV5Utils.singletonV5; } /** * 静态内部方式能够避免同步带来的效率问题和有能实现延迟加载 */ public static class SingletonV5Utils { private static SingletonV5 singletonV5 = new SingletonV5(); } public static void main(String[] args) { System.out.println("项目启动成功"); SingletonV5 instance1 = SingletonV5.getInstance(); SingletonV5 instance2 = SingletonV5.getInstance(); System.out.println(instance1 == instance2); } }
枚举形式
枚举形式能够先天性,防止反射和序列化破解单例。
public enum EnumSingleton { INSTANCE; // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解 public void add() { System.out.println("add方法..."); } } public static void main(String[] args) throws Exception { EnumSingleton instance1 = EnumSingleton.INSTANCE; EnumSingleton instance2 = EnumSingleton.INSTANCE; System.out.println(instance1 == instance2); Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); EnumSingleton v3 = declaredConstructor.newInstance(); System.out.println(v3==instance1); }
使用容器管理
public class SingletonManager { private static Map<String, Object> singletonMap = new HashMap<>(); public static void registerService(String key, Object instance) { if (key.length()>0 && instance != null) { if (!singletonMap.containsKey(key)) { singletonMap.put(key, instance); } } } public static Object getService(String key) { return singletonMap.get(key); } public static void main(String[] args) { System.out.println("项目启动成功"); SingletonManager.registerService("object",new Object()); Object instance1 = SingletonManager.getService("object"); Object instance2 = SingletonManager.getService("object"); System.out.println(instance1 == instance2); } }
这种使用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
虽然单例通过私有构造函数,可以实现防止程序猿初始化对象,但是还可以通过反射和序列化技术破解单例。
使用反射技术破解单例
// 1. 使用懒汉式创建对象
SingletonV3 instance1 = SingletonV3.getInstance();
// 2. 使用Java反射技术初始化对象 执行无参构造函数
Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV3 instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);
如何防止被反射破解
private SingletonV3() throws Exception {
synchronized (SingletonV3.class) {
if (singletonV3 != null) {
throw new Exception("该对象已经初始化..");
}
System.out.println("执行SingletonV3无参构造函数...");
}
}
使用序列化技术破解单例
public class Test002 {
public static void main(String[] args) throws Exception {
// 1.需要将该对象序列化到本地存放
FileOutputStream fos = new FileOutputStream("d:/code/user.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
Singleton01 instance1 = Singleton01.getInstance();
oos.writeObject(instance1);
oos.close();
fos.close();
//2.从硬盘中反序列化对象到内存中
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/code/user.txt"));
Singleton01 instance2 = (Singleton01) ois.readObject();
System.out.println(instance1==instance2);
}
}
如何防止序列化破解
单例对象重写该方法 指定返回的对象 防止序列化破解
//返回序列化获取对象 ,保证为单例
public Object readResolve() {
return singletonV3;
}
注意:如果该类是Serializable类型的 则调用它第一个非Serializable父类的无参构造函数初始化该对象该对象
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。