Java单例模式实现方式

来源:互联网 发布:武汉光谷相关数据 编辑:程序博客网 时间:2024/06/16 04:04

实现方式一览

  1. 第一种(懒汉,线程不安全)

    public class Singleton {      private static Singleton instance;    private Singleton (){}    public static Singleton getInstance() {          if (instance == null) {              instance = new Singleton();          }          return instance;      }  }
  2. 第二种(懒汉,线程安全)

    public class Singleton {      private static Singleton instance;      private Singleton (){}      public static synchronized Singleton getInstance() {      if (instance == null) {          instance = new Singleton();      }      return instance;      }  }

    这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

  3. 第三种(懒汉,双重校验锁)

    public class Singleton {    private volatile static Singleton singleton;    private Singleton (){}     public static Singleton getSingleton() {          if (singleton == null) {              synchronized (Singleton.class) {                  if (singleton == null) {                      singleton = new Singleton();                  }            }          }          return singleton;      }  } 

    在JDK1.5之后,双重检查锁定才能够正常达到单例效果。因为1.5之前的volatile关键字没有正确的实现其语义(存在乱序写入问题,即编译器重排优化,参考:Java单例模式中双重检查锁的问题)。

  4. 饿汉模式Ⅰ(静态属性直接初始化)

    public class Singleton {    private static Singleton instance = new Singleton();    private Singleton (){}    public static Singleton getInstance() {          return instance;      }  }
  5. 饿汉模式Ⅱ静态属性静态块中初始化)

    public class Singleton {    private static Singleton instance;    static {        instance = new Singleton();    }        private Singleton (){}     public static Singleton getInstance() {          return instance;      }  }

    4和5中的本质是一样的,基于classloder机制避免了多线程的同步问题。一般认为静态属性直接赋值和static块在类加载后就会执行,这是错误的认识,而是在类被主动使用时才会执行(初始化)。详见http://blog.csdn.net/berber78/article/details/46472789

  6. 静态内部类

    public class Singleton {    private static class SingletonHolder {          private static final Singleton INSTANCE = new Singleton();      }    private Singleton (){}    public static final Singleton getInstance() {          return SingletonHolder.INSTANCE;      }  } 

    此模式与4和5保证线程安全的原理一样,只不过加了懒加载功能,因为私有静态内部类截断了其他触发初始化操作的条件,只剩下一个条件:访问SingletonHolder类的静态属性INSTANCE,也就是我们的单例对象。

  7. 枚举

    public enum Singleton {      INSTANCE;      public void whateverMethod() {    }  }

    “这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象”
    上面是网上的人崇拜大神说的,其实他不明白其中的原理,上面的代码等价于:

     public class Singleton extends Enum<Singleton> {    public static final Singleton[] values() {        return (Color[])$VALUES.clone();     }    public static Singleton valueOf(String name) {        //return $VALUES[indexOfname];大概逻辑    }    private Singleton(String s, int i) {        super(s, i);    }    public static final Singleton INSTANCE;    private static final Color $VALUES[];    static {        INSTANCE = new Color("INSTANCE", INSTANCE);        $VALUES = (new Color[] { INSTANCE});    }}

    自习分析,方向跟4和5有异曲同工之妙。但是在您不想使用INSTANCE时千万不要调用valueOf和values方法,因为您的意图不是使用INSTANCE,而此时却加载了实例。请不要在枚举类里面写静态方法和其他静态属性,因为你一旦访问,就会创建实例,违背了懒加载的初衷。而enum被推崇的另外一个原因是反序列化不会创建新对象,因为Enum类里的三个方法:

    /** * prevent default deserialization */private void readObject(ObjectInputStream in) throws IOException,    ClassNotFoundException {    throw new InvalidObjectException("can't deserialize enum");}private void readObjectNoData() throws ObjectStreamException {    throw new InvalidObjectException("can't deserialize enum");}

    不是两个吗?你骗人!还有一个readResolve隐式实现了,readObject和readObjectNoData都直接抛出了异常,序列化机制明白是要阻止默认的反序列化方式,就会调用readResolve,readResolve返回一个匹配的对象就行了,我想java内部对于enum类型的这个方法的实现应该是直接返回INSTANCE吧!
    Effective Java作者Josh Bloch不会没发现这些问题吧?他应该发现了,只不过大神都懒得解释,因为对于规范使用枚举的人,一般不会出现那些问题。Josh Bloch应该是从简洁、不易出错、高性能的角度认为这是最好的方式。但是这种方式比较诡异,感觉枚举被用坏了似的。

总结

方式1单线程可用,但是单线程的程序很少,不能实现线程安全不推荐使用
方式2多线程可用,但是性能不好,不是每次访问都需要加上锁
方式3多线程可用,java 1.5版本之后可用
方式4和5多线程可用,但是不会懒加载
方式6多线程可用,very good
方式7简洁诡异,但是需要注意那几个点

原创粉丝点击