单例模式
属于创建型设计模式,为了方便的得到唯一对象,起到了全局统一管理的作用。
接下来介绍单例模式的写法。
饿汉模式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}
1.类的构造函数定义为private,保证其他类不能实例化此类。其他的单例类写方法也是这样的。
2.通过静态变量返回一个实例对象,我们知道静态变量的创建只会在类加载的时候创建一次,保证了唯一。
3.缺点:上来就创建对象,即使这个单例没有用到也会创建(类加载器并不需要等到某个类被首次使用到再加载它)。占内存。
懒汉模式
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
1.存在问题。在多个线程可能会并发的调用newInstance方法,导致创建多个实例。
2.优点:延时加载
双重校验锁(DCL)
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {//2
instance = new Singleton();
}
}
}
return instance;
}
}
第一次判断空,避免执行到同步代码块,提高性能。第二次判断空,在同步代码下,再次判断确保唯一实例。
缺点:这种方式仍然不能保证线程安全,原因是在instance = new Single();这句话在Jvm里的执行是分三步的。
-
在堆内开辟内存空间。
-
在堆内存中实例化Single里面的各个参数。
-
把对象指向内存空间。
由于Jvm存在乱序执行功能,所有可能第二步还没执行就先只执行了三,此时如果切换线程,由于执行了3,instance已经非空了 ,会被直接拿来用,这时候就会出现异常。
在JDK1.5后,只要加上了volatile,保证了instance每次均从主内存中读取,就可以解决DCL失效问题。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}
这种方式同样利用了类加载机制来保证只创建一个instance实例。它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载。也就是说这种方式可以同时保证延迟加载和线程安全。
枚举
上面提到的四种实现单例的方式都有共同的缺点:
1)需要额外的工作来实现序列化,否则每次反序列化一个序列化的对象时都会创建一个新的实例。
2)可以使用反射强行调用私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
而枚举类很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。不过,在实际工作中,很少看见有人这么写。
public enum Singleton{
instance;
public void whateverMethod(){}
}