Java 单例模式

来源:互联网 发布:笔趣阁 知乎 编辑:程序博客网 时间:2024/06/11 10:49

Java中的单例模式

类的单例模式在编程中运用的非常广泛,所谓单例模式就是控制一个类只能产生一个对象,在堆空间开辟一块内存,所有的引用都指向它。
单例模式的实现可以分几种情况,其共同的基础是控制构造函数私有化,一个私有静态的单例类变量,公共静态的获取单例变量的方法。
一般单例模式的写法:
public class Singleton { //类的单例模式private static Singleton instance = null;private Singleton(){}public static Singleton newInstance() {if(instance == null) {instance = new Singleton();}return instance;}}
这种写法在多线程情况下会引发一个不确定性,当我们的一个线程在调用上面的代码instance为空时,如果在判断的时候被暂停了,也就是说instance==null的条件是成立的,这个时候有第二个线程也在调用这段代码,这个时候instance的值依然为空所以也是可以进入判断条件的。这种情况下就会有多个Singleton类被实例化了,从而导致系统的不稳定性。我们怎样来解决这个问题了?

我们有两种方法:饿汉式单例和懒汉式单例
1、饿汉式单例,当类被加载时,静态变量instance会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例将被创建。
class EagerSingleton {       private static final EagerSingleton instance = new EagerSingleton();       private EagerSingleton() { }         public static EagerSingleton getInstance() {          return instance;       }     }  
2、懒汉式单例:既然是在多线程的情况下,相信很多童鞋都会想到上锁机制;在这种模式下我们使用了synchronized关键字;
public static LazySingleton getInstance() {       if (instance == null) {          synchronized (LazySingleton.class) {              instance = new LazySingleton();           }      }      return instance;   } 

但是即使在这种情况下依然无法保证单例对象唯一。于是我们有想到了双重检查锁定(Double-Check Locking)技术,在synchronized中再进行一次(instance == null)判断;但是双重检查锁定需要在静态成员变量instance之前增加修饰符volatile;从而导致JVM不会做相应的代码优化。


所以我们有了下面的代码
class LazySingleton {       private volatile static LazySingleton instance = null;         private LazySingleton() { }         public static LazySingleton getInstance() {           //第一重判断          if (instance == null) {              //锁定代码块              synchronized (LazySingleton.class) {                  //第二重判断                  if (instance == null) {                      instance = new LazySingleton(); //创建单例实例                  }              }          }          return instance;       }  } 
比较:饿汉式单例是在类加载的时候实例化单例对象的,无需考虑多线程的问题,但是浪费资源。而懒汉式单例则可以实现延时加载,但是必须处理多线程问题,而且损失了JVM的代码优化功能。
有没有一种平衡这两种方式的方法了?
答案是肯定的,那就是我们的终极单例模式解决方案IoDH(Initialization Demand Holder);在该内部类中创建单例对象再将该单例对象通过方法返回给外部使用。这种方法既可以实现延时加载,又可以保证线程安全,又不影响性能。

//Initialization on Demand Holder  class Singleton {      private Singleton() {      }            private static class HolderClass {              private final static Singleton instance = new Singleton();      }            public static Singleton getInstance() {          return HolderClass.instance;      }            public static void main(String args[]) {          Singleton s1, s2;               s1 = Singleton.getInstance();          s2 = Singleton.getInstance();          System.out.println(s1==s2);      }  }  
参考文献:http://blog.csdn.net/lovelion/article/details/7420883里面的一系列有关设计模式的好文章;

0 0