转载请注明出处:http://cantellow.iteye.com/blog/838473
第一种(懒汉,线程不安全):
- public class Singleton {
- private static Singleton instance; //static会在编译时初始化
- private Singleton (){}
- public static Singleton getInstance() {
- if (instance == null) {
- instance = new Singleton();
- }
- return instance;
- }
- }
这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。
第二种(懒汉,线程安全):
- 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(); //static会在编译时由jvm初始化一次
- private Singleton (){}
- public static Singleton getInstance() {
- return instance;
- }
- }
这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。
第四种(饿汉,变种):
- public class Singleton {
- private Singleton instance = null;
- static {
- instance = new Singleton();
- }
- private Singleton (){}
- public static Singleton getInstance() {
- return this.instance;
- }
- }
表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。
第五种(静态内部类):
- 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就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。
第六种(枚举):
- public enum Singleton {
- INSTANCE;
- public void whateverMethod() {
- }
- }
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。
第七种(双重校验锁):
- public class Singleton {
- private volatile static Singleton singleton; //volatile使用共享内存存放变量 singleton,线程不会访问它自己的内存空间
- private Singleton (){}
- public static Singleton getSingleton() {
- if (singleton == null) {
- synchronized (Singleton.class) {
- if (singleton == null) { //当两个线程都通过第一个if时,此步很有必要
- singleton = new Singleton();
- }
- }
- }
- return singleton;
- }
- }
这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5之后,双重检查锁定才能够正常达到单例效果。
总结
有两个问题需要注意:
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;
- }
- }
对我来说,我比较喜欢第三种和第五种方式,简单易懂,而且在JVM层实现了线程安全(如果不是多个类加载器环境),一般的情况下,我会使用第三种方式,只有在要明确实现lazy loading效果时才会使用第五种方式,另外,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证我的程序是线程安全的,而且我永远不会使用第一种和第二种方式,如果有其他特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。
========================================================================
superheizai同学总结的很到位:
不过一般来说,第一种不算单例,第四种和第三种就是一种,如果算的话,第五种也可以分开写了。所以说,一般单例都是五种写法。懒汉,恶汉,双重校验锁,枚举和静态内部类。
我很高兴有这样的读者,一起共勉。
相关推荐
单例模式确保一个类只有一个实例,并提供一个全局访问点。在Java中,通常通过私有构造函数和静态工厂方法来实现。单例模式常用于配置中心、线程池等场景,确保系统资源的有效利用。 二、工厂模式 工厂模式是一种...
* 单例模式:单例模式是一种创建型模式,它保证一个类只有一个实例,并提供一个访问该实例的全局点。 * 建造者模式:建造者模式是一种创建型模式,它提供了一种创建复杂对象的方式,而不需要指定它们的类。 * 原型...
文章接着介绍了 Mybatis 框架源码中的 10 种设计模式应用,包括工厂模式、单例模式、原型模式、建造者模式、代理模式、适配器模式、桥模式、组合模式、享元模式和解释器模式。这些设计模式的应用能够帮助读者更好地...
5. **单例模式**:确保一个类只有一个实例,并提供一个全局访问点,常用于配置中心或缓存管理。 **结构型模式**关注的是如何组合类和对象以构成更复杂的结构。共有七种结构型模式: 1. **代理模式**:在对象访问...
1. 创建型模式(Creational Patterns):这五种模式关注对象的创建过程,包括工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。它们帮助开发者在不指定具体类的情况下创建对象,使得代码更具灵活性。 ...
1) 内容包括: 设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种...
创建型模式关注对象的创建和实例化,共有五种:工厂方法模式、抽象工厂模式、建造者模式、单例模式和原型模式。 * 工厂方法模式:提供一个创建对象的接口,-delaying 到子类中实现具体的创建逻辑。例如, ...
* 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问该实例的全局点。 * 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 * 原型模式...
以下是关于这七种设计模式的详细解释和实例: 1. 单例模式(Singleton): 单例模式确保一个类只有一个实例,并提供一个全局访问点。这在资源管理(如数据库连接)或全局配置时非常有用。以下是一个简单的单例模式...
一、单例模式(Singleton) 单例模式确保一个类只有一个实例,并提供一个全局访问点。在C#中,可以通过静态成员或者 Lazy<T> 类来实现单例,以保证线程安全并延迟初始化。 二、工厂模式(Factory) 工厂模式是一种...
创建型模式主要用于对象的创建过程,关注对象创建的细节,以便于系统在运行时能够灵活地创建对象,常见的创建型模式有六种:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式和单例模式。...
3. 单例模式(Singleton):保证一个类仅有一个实例,并提供一个全局访问点。 4. 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 5. 原型模式(Prototype):...
第五讲:单例模式 第六讲:原型模式 第七讲:建造者模式 第八讲:装饰模式 第九讲:策略模式 第十讲:观察者模式 第十一讲:享元模式 第十二讲:代理模式 第十三讲:外观模式 第十四讲:组合模式 第十五讲...
单例模式的定义是“确保一个类只有一个实例,并提供一个全局访问点”。 4. 代理模式 代理模式是 MyBatis 中使用的第四个设计模式。例如 MapperProxy、ConnectionLogger 就是代理模式的应用。代理模式的定义是“为...
一、单例模式(Singleton) 单例模式确保一个类只有一个实例,并提供一个全局访问点。在Java中,可以通过枚举、双重检查锁定(Double-Checked Locking)或者静态内部类等方式实现单例。这种模式常用于配置中心、...
单例模式确保一个类只有一个实例,并提供一个全局访问点。在C++中,可以通过私有化构造函数和静态成员函数来实现单例。这种模式常用于控制资源的全局访问,如日志系统、线程池等。 二、工厂模式(Factory) 工厂...