关于后端:详解三种单例模式的实现

2次阅读

共计 2389 个字符,预计需要花费 6 分钟才能阅读完成。

培养能力的事必须持续一直地去做,又必须随时改善学习办法,进步学习效率,才会胜利。

单例模式的定义:保障一个类仅有一个实例,并提供一个拜访它的全局拜访点!

懒汉

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% 状况下不须要同步。

饿汉

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

    private Singleton() {}

    public static Singleton getInstance() {return instance;}
}

这种形式基于 classloder 机制防止了多线程的同步问题,instance 在类装载时就实例化。目前 java 单例是指一个虚拟机的范畴,因为装载类的性能是虚拟机的,所以一个虚拟机在通过本人的 ClassLoader 装载饿汉式实现单例类的时候就会创立一个类的实例。

这就意味着一个虚拟机外面有很多 ClassLoader,而这些 Classloader 都能装载某个类的话,就算这个类是单例,也能产生很多实例。当然如果一台机器上有很多虚拟机,那么每个虚拟机中都有至多一个这个类的实例的话,那这样就更不会是单例了。(这里探讨的单例不适宜集群!)

动态外部类

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

    private Singleton() {}

    public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}
} 

这种形式同样利用了 classloder 的机制来保障初始化 instance 时只有一个线程,这种形式是 Singleton 类被装载了,instance 不肯定被初始化。因为 SingletonHolder 类没有被被动应用,只有显示通过调用 getInstance 办法时,才会显示装载 SingletonHolder 类,从而实例化 instance。

设想一下,如果实例化 instance 很耗费资源,我想让他提早加载!这个时候,这种形式相比第 2 种形式就显得很正当。

枚举

public enum Singleton {
    INSTANCE;

    public void whateverMethod() {}
}

这种形式是 Effective Java 的作者 Josh Bloch 提倡的形式,它不仅能防止多线程同步问题,而且还能避免反序列化从新创立新的对象,不过,集体认为因为 1.5 中才退出 enum 个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少看见有人这么写过。

双重校验锁 (jdk1.5)

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;
    }
}

这样形式实现线程平安地创立实例,而又不会对性能造成太大影响。它只是第一次创立实例的时候同步,当前就不须要同步了。

因为 volatile 关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因而倡议没有特地的须要不要应用。双重测验锁形式的单例不倡议大量应用,依据状况决定。

总结

有两个问题须要留神:

  1. 如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假设不是远端存取,例如一些 servlet 容器对每个 servlet 应用齐全不同的类装载器,这样的话如果有两个 servlet 拜访一个单例类,它们就都会有各自的实例。
  2. 如果 Singleton 实现了 java.io.Serializable 接口,那么这个类的实例就可能被序列化和还原。不管怎样,如果你序列化一个单例类的对象,接下来还原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的方法是:

private static Class getClass(String classname)
        throws ClassNotFoundException {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

    if (classLoader == null)
        classLoader = Singleton.class.getClassLoader();

    return (classLoader.loadClass(classname));
}

对第二个问题修复的方法是:

public class Singleton implements java.io.Serializable {public static Singleton INSTANCE = new Singleton();

    protected Singleton() {}

    private Object readResolve() {return INSTANCE;}
}
正文完
 0