完美的单例模式创建
今天把设计模式拿出来看了下,发现以前对于单例模式的理解很是肤浅,没考虑线程安全,在考虑了线程安全的情况下又没考虑性能,当然我遇到的系统都不怎么考虑并发性能,所以其实也无所谓,当看到单例模式的时候,上网搜索了下,发下一片很好的帖子,我把其中的单例模式整理了下,给了个结果出来。
帖子地址:http://blog.csdn.net/zhangerqing/article/details/8194653
前提都是在考虑线程安全的情况下,可以有两种情况。分别是懒汉式和饿汉式的一种实现。
1.性能不太好,每次锁对象,当然如果你遇到的跟我遇到的系统都一样不考虑性能问题,其实这么做也没问题,但是不好。正如Oracle高级编程里面所说的那样,你的sql能正确的完成任务,但并不代表他能很好的完成任务。
2.我们只希望在第一次getInstance的时候去实例化对象,这个就做到了,并且线程安全,但是如果类加载的时候出问题还是有可能出问题,所以借用那篇帖子里说的,要完成完美的实现是不可能的,我们只能根据实际情况来选择合适的方式。
1.直接锁整个对象,保证线程安全,但是由于每次获取都会锁对象,性能不好,也不是我们希望的。
package singlePattern; /** * Thread safe single pattern, but performance is poor, because every time we will lock the instance, * we hope lock the instance on first time but all times * * @author Administrator * */ public class SinglePattern { // private instance private static SinglePattern instance = null; // private constructor private SinglePattern() { } // public getInstance method public synchronized static SinglePattern getInstance() { if (instance == null) { instance = new SinglePattern(); } return instance; } }
2.我们需要的情形是在第一次初始化需要同步,之后的调用就不需要考虑线程同步。由于JVM在加载类的时候是互斥的,我们知道类加载器在加载类的时候会直接把静态变量和静态代码块执行一次,然后再去去调用普通代码快,然后去调用构造方法初始化堆,然后在返回引用赋值给栈里面的引用。于是我们可以利用类加载的静态变量和静态代码块来安全的构造单例实例。
package singlePattern; /** * Thread safe and performance good. * @author Administrator * */ public class UserSinglePattern { // private constructor private UserSinglePattern() {} // static inner class, for create a single pattern instance private static class NewInstance { private static UserSinglePattern instance = new UserSinglePattern(); } // the get method for single pattern instance public static UserSinglePattern getInstance() { return NewInstance.instance; } }
3.如果实例采用静态的就能实现在类加载的时候线程安全的初始化实例,但是却不能实现lazy加载,而采用上面的静态内部类的方式就能实现lazy加载,因为虚拟机机制是需要调用getInstance()方法的时候才会加载静态内部类,如果不调用那么他是不会被加载的。下面是测试代码,同时拥有2个静态内部类来创建对象,但是只调用其中一个那么就只会加载一个,另外一个是不会被加载的。
public class SinglePatternThreadSafe { // 私有构造方法 private SinglePatternThreadSafe() { System.out.println(123); } private SinglePatternThreadSafe(String str) { System.out.println(321); } // 内部类,利用类加载的互斥性来达到单例的创建 private static class NewInstance { private static SinglePatternThreadSafe instance = new SinglePatternThreadSafe(); } private static class NewInstance1 { private static SinglePatternThreadSafe in = new SinglePatternThreadSafe("11"); } // 返回实例的公共方法 public static SinglePatternThreadSafe getInstance() { return NewInstance.instance; } public static SinglePatternThreadSafe getInstance(String str) { return NewInstance1.in; } public static void main(String[] args) { SinglePatternThreadSafe.getInstance(); }
// 只打印出123而没有打印11 }
再贴下我参考的地址对作者表示感谢:http://blog.csdn.net/zhangerqing/article/details/8194653
这个也写的很好:http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html
这篇也测试了如果在不调用静态内部类的方法或者变量或者常量的时候内部类是不会被加载的,http://yongliang567.iteye.com/blog/904467