单例设计模式
来源:互联网 发布:手机淘宝扫一扫 编辑:程序博客网 时间:2024/04/30 06:05
单例设计模式的常见场景
单例设计模式的五种实现
- 懒汉模式
package com.pattern.gof;/** * 测试懒汉模式 * * 类加载器在加载的时候,就new 一个实例,非延时加载 * * @author Nicholas * */public class SingletonDemo2 { /** * 1、构造器私有 2、提供静态属性,不初始化 3、提供静态的访问接口,要同步 这是个懒加载,用的时候,才去创建 效率不高,多个线程,必须同步 * */ private static SingletonDemo2 instance = null; // 1、构造器私有 private SingletonDemo2() { } // 3、提供静态的访问接口,需要synchronized同步,防止创建多个对象 public static synchronized SingletonDemo2 GetInstance() { if (instance == null) instance = new SingletonDemo2(); return instance; }}
- 饿汉模式
package com.pattern.gof;import java.io.Serializable;/** * 测试饿汉式单例模式 * * 类加载器在加载的时候,就new 一个实例,非延时加载 * * @author Nicholas * */public class SingletonDemo1 implements Serializable { /** * @Fields serialVersionUID */ private static final long serialVersionUID = 1L; /** * 1、构造器私有 2、提供静态属性 同时new 初始化 3、提供静态的访问接口,不需要同步 * * 类加载器加载一个类的时候,是天然的线程安全模式。所以饿汉式是线程安全的,效率也很高。 * * 资源利用效率不高。 */ // 2、提供静态属性 private static SingletonDemo1 instance = new SingletonDemo1(); // 1、构造器私有 private SingletonDemo1() { } // 3、提供静态的访问接口,不需要synchronized同步 public static SingletonDemo1 GetInstance() { return instance; }}
- 双重检测锁
package com.pattern.gof;/** * 双重检测锁实现 延时加载 * 由于编译器优化,可能会出现问题 * @author Nicholas * * 同步代码快而不是同步方法,效率有所提高 * 只在第一次加载的时候同步,后面不同步 */public class SingletonDemo3 { private static SingletonDemo3 instance = null; private SingletonDemo3() { } public static SingletonDemo3 getInstance() { if (instance == null) { SingletonDemo3 singletonDemo_3; synchronized (SingletonDemo3.class) { singletonDemo_3 = instance; if (singletonDemo_3 == null) { synchronized (SingletonDemo3.class) { if (singletonDemo_3 == null) { singletonDemo_3 = new SingletonDemo3(); } } instance = singletonDemo_3; } } } return instance; }}
- 静态内部类
package com.pattern.gof;/** * 静态内部类实现 ,也是懒加载 * * @author Nicholas instance是static final类型,确保了只有一个,所以是绝对的线程安全的。 同时兼备懒加载和高并发的优势。 * 只有调用getInstance()时,才会去加载 */public class SingletonDemo4 { private static class SingletonDemo_4Instance { private static final SingletonDemo4 instance = new SingletonDemo4(); } // 构造器私有 private SingletonDemo4() { } // 提供对外的接口 public static SingletonDemo4 getInstance() { return SingletonDemo_4Instance.instance; }}
- 枚举
package com.pattern.gof;/** * 枚举本身就是单例,这个是立即加载,实现也比较简单 * * @author Nicholas * */public enum SingletonDemo5 { // 定义一个枚举变量,代表SingletonDemo_5的一个实例 INSTANCE; public static void main(String[] args) { SingletonDemo5 singletonDemo1 = SingletonDemo5.INSTANCE; SingletonDemo5 singletonDemo2 = SingletonDemo5.INSTANCE; System.out.println(singletonDemo1 == singletonDemo2); }}
枚举模式的漏洞(除了枚举)
- 反射破解单例
package com.pattern.gof;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/** * @ClassName: TestOfSingleton * @Description: 测试单例模式的懒汉模式,在序列化和反序列化时会出现异常 * @author 韦轩 * @date 2015年7月12日 下午5:57:53 * */public class TestOfSingleton { public static void main(String[] args) { SingletonDemo1 singletonDemo1 = SingletonDemo1.GetInstance(); SingletonDemo1 singletonDemo2 = SingletonDemo1.GetInstance(); System.out.println(singletonDemo1); System.out.println(singletonDemo2); System.out.println(singletonDemo1 == singletonDemo2); // 通过反射的方式破解单例模式(除枚举的方式之外) try { @SuppressWarnings("unchecked") Class<SingletonDemo1> class1 = (Class<SingletonDemo1>) Class .forName("com.pattern.gof.SingletonDemo1"); // 获取无参构造器 Constructor<SingletonDemo1> constructor = class1 .getDeclaredConstructor(null); /** * java.lang.IllegalAccessException: */ constructor.setAccessible(true); SingletonDemo1 s1 = constructor.newInstance(); SingletonDemo1 s2 = constructor.newInstance(); /** * s1和s2会出现不一致的状况 如何解决这个问题? 在私有的构造器中判断,如果instance不为空,抛出异常 */ System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } }}
输出结果,通过普通方式创建的对象是同一个,但是通过反射创建的对象,会出现不一致。解决办法是在私有的构造器中判断instance是否为空,抛出异常
com.pattern.gof.SingletonDemo1@659e0bfdcom.pattern.gof.SingletonDemo1@659e0bfdtruecom.pattern.gof.SingletonDemo1@2a139a55com.pattern.gof.SingletonDemo1@15db9742false
解决办法
private CopyOfSingletonDemo1() { /** * 用抛出异常的形式来屏蔽反序列化中创建多个实例 */ if (instance != null) throw new RuntimeException("Can't create another object ..."); }
- 反序列化破解枚举
package com.pattern.gof;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;/** * @ClassName: TestOfSingleton * @Description: 测试单例模式的懒汉模式,在序列化和反序列化时会出现异常 * @author 韦轩 * @date 2015年7月12日 下午5:57:53 * */public class TestOfSingleton { public static void main(String[] args) { SingletonDemo1 singletonDemo1 = SingletonDemo1.GetInstance(); SingletonDemo1 singletonDemo2 = SingletonDemo1.GetInstance(); System.out.println(singletonDemo1); System.out.println(singletonDemo2); System.out.println(singletonDemo1 == singletonDemo2); // 通过序列化将对象写入到硬盘 try { FileOutputStream fileOutputStream = new FileOutputStream( "D:\\J2EE\\GOF_pattern\\object.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream( fileOutputStream); System.out.println(singletonDemo1); objectOutputStream.writeObject(singletonDemo1); } catch (IOException e) { e.printStackTrace(); } // 进行反序列化 try { ObjectInputStream objectInputStream = new ObjectInputStream( new FileInputStream("D:\\J2EE\\GOF_pattern\\object.txt")); SingletonDemo1 sd1 = (SingletonDemo1) objectInputStream.readObject(); System.out.println(sd1); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } }}
这里,我们是将singletonDemo1序列化到硬盘,然后发序列化,
结果是不一致的状况
com.pattern.gof.SingletonDemo1@659e0bfdcom.pattern.gof.SingletonDemo1@3d4eac69
这种情况的解决办法是在单例模式的实现类中试下一个方法,当反序列化时,直接返回已经有的对象
/** * * @Title: readResolve * @Description: 在反序列化时,直接返回已经创建的实力对象 * @param @return * @param @throws ObjectStreamException * @return Object * @throws */ private Object readResolve() throws ObjectStreamException { return instance; }
修改后的枚举实现
这里只实现了饿汉式
package com.pattern.gof;import java.io.ObjectStreamException;import java.io.Serializable;/** * * @ClassName: NewSingleton * @Description: 单例模式的饿汉式实现* @author 韦轩* @date 2015年7月12日 下午7:27:13 * */public class NewSingleton implements Serializable { /** * @Fields serialVersionUID */ private static final long serialVersionUID = 1L; private static NewSingleton instance = new NewSingleton(); private NewSingleton() { //屏蔽反射创建多个对象,出现不一致的情况 if (instance != null) throw new RuntimeException("Can't create another object ..."); } public static NewSingleton GetInstance() { return instance; } /** * * @Title: readResolve * @Description: 在反序列化时,直接返回已经创建的实例对象 * @param @return * @param @throws ObjectStreamException * @return Object */ private Object readResolve() throws ObjectStreamException { return instance; }}
五种实现的对比
效率测试
package com.pattern.gof;import java.util.concurrent.CountDownLatch;/** * * @ClassName: TestEfficiency * @Description: 测试五种单例模式的效率 * @author 韦轩 * @date 2015年7月12日 下午6:39:37 * */public class TestEfficiency { public static void main(String[] args) { long startTime = System.currentTimeMillis(); int threadNumber = 10; CountDownLatch countDownLatch = new CountDownLatch(threadNumber); /** * 开启十个线程,每个线程调用SingletonDemo1.GetInstance();10W次 */ for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 100000; i++) { Object object = SingletonDemo1.GetInstance(); //16 //Object object = SingletonDemo2.GetInstance();//49 //Object object = SingletonDemo3.getInstance(); //11 //Object object = SingletonDemo4.getInstance(); //19 //Object object = SingletonDemo5.INSTANCE; //11 } countDownLatch.countDown(); } }).start(); } //等待执行完成 try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime = System.currentTimeMillis(); System.out.println("Total time :" + (endTime - startTime)); }}
0 0
- 设计模式--单例
- 单例设计模式
- 设计模式----单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 设计模式-单例
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- 单例设计模式
- ACM--Expanding Fractions
- STL容器的适用情况
- 执行qmake的时候发生错误
- 线段树——HDOJ 1166 敌兵布阵 解题报告
- Apache Spark入门攻略
- 单例设计模式
- 经典makefile例子
- Android WebView的简单使用
- 第一次电话面试感想
- 软件内每日提醒功能,可整合到工程中
- js中select动态添加option
- 本周周报
- iOS 9 适配系列教程
- linux下C语言socket网络编程简例-(转载)