一直觉得单例模式是Gof 23种设计模式中最简单的,但这些天恰巧看到一些关于单例模式的书和文章,才意识到单例模式也可以挖掘出很多知识,而且可以开阔我们处理问题的思路。
实现单例模式我找到大致三种方法:通过公有静态final域实现、通过单元素枚举类型实现、通过静态工厂实现。
首先来简单看一下公有静态final域和单元素枚举实现方式,因为这两种方式都非常简洁且易于理解。最后着重分析我们使用最广泛的通过静态工厂单例模式的实现。
通过公有静态final域实现单例模式的代码是这样的简单:
public class Singleton {
public static final Singleton INSTANCE = new Singleton();
private Singleton(){}
}
这种实现方式的缺点是欠缺灵活性。类直接将它的静态域提供给客户端,不能根据需要提供其它实例。如果类是通过方法提供给客户端实例,则方法返回的实例可以是其它实现(这要求该类有父类或实现接口),甚至可以改变其本质是否为一个真的单例而不需要改变其对外提供的API。而且现代的JVM通过内联静态工厂方法消除了静态工厂单例实现相比于公有静态final域实现的性能劣势。
单元素枚举类型是一个非常好的实现单例模式的方法,示例代码是这样的:
public enum Singleton {
INSTANCE
public void otherMethod(){...}
}
这种方法可能还没有被广泛的采用,不过它是实现Singleton非常好的方式,我想不出这种方法有任何实质性的缺点。鼓励大家尝试应用这种实现方式。
现在来看一下我们最常用的通过静态工厂方法实现单例模式的情况。常用的形式有下面几种:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
一般情况这种方式是很不错的,结构简洁,像我们预想的一样工作,不会出什么问题。但有一种思想也被广泛应用,那就是延迟初始化。
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
延迟初始化的使用者想要在实例没有被真正引用前不执行初始化,只有实例真正被引用才让静态工厂方法去初始化单例类的实例。这种做法绝大多数情况都只是将初始化类的开销转移到了访问实例时,根据经验,这种优化往往是得不偿失的。更糟糕的情况出现在多线程的应用场景中。为了防止多线程同时实例化instance,就需要给工厂方法加上synchronized同步,线程同步即使在现在的JVM中仍然是相对昂贵的。每次调用该方法都将导致线程同步,显然是不合算的。
为了解决这个问题,可以只将实例化过程同步,代码如下:
private static Singleton instance;
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
这种实现事实上并没有解决多线程可能实例化多个对象的问题。比如线程1和线程2同时通过if判断,虽然同步块阻止了两个线程同时创建对象,但这两个线程仍然会先后执行同步块中的代码,创建出两个对象。
一种叫做双重检测的技术被一些开发者应用来试图解决这个问题。
public static Singleton getInstance() {
if(instance == null) {
synchronized(Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
但不幸的是这种复杂的检测机制仍然有可能失败,原因在于instance = new Singleton()并不是原子性的。在大部分的JVM实现中,这一语句被分成三步来完成:
1.为instance分配内存空间
2.在分配到的空间上执行构造方法
3.将instance指向该内在空间
又由于java的内存模型允许”无序写入“,所以2和3的顺序并不能保证,也就是说instance可能还没有被调用构造方法进行初始化就已经是非null的了。这样可能的结果就是第二个线程读取到这个不完整实例,并将其返回给客户端。情况变得更糟了!如果你执意要这么做,那就要给静态域加上volatile关键字,访问volatile变量就像变量自身处在一个同步块中一样。但要注意volatile的准确语意是在java1.5后才完整支持的。
哦,天啊,希望你不是如此的钟爱这种方法!我的建议是不到万不得已不要使用延迟初始化,如果在极少数情况下经过测试证明延迟初始化的确能带来大的性能优化,那么这里提供最后一种方式做到这一点。
public class Singleton {
private Singleton(){}
private static class Builder {
static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Builder.instance;
}
}
总而言之,用单元素枚举来实现单例模式吧,如果你实在觉得它怪异而采用静态工厂的方式,也尽量不要选用延迟加载。
分享到:
相关推荐
### JAVA单例模式的几种实现方法 #### 一、饿汉式单例类 饿汉式单例类是在类初始化时就已经完成了实例化的操作。这种实现方式简单且线程安全,因为实例化过程是在编译期间完成的,不会受到多线程的影响。 **代码...
这个讲的是单例模式的多种不同实现方式,希望对单例感兴趣的同学看看
在多线程环境下,我们需要确保单例实例的创建只发生一次,以上几种方式除了懒汉式(线程不安全)外,都能保证线程安全。对于反序列化测试,由于默认反序列化会生成新实例,需要重写 `readResolve()` 方法来防止这种...
下面将详细介绍单例模式的几种常见实现方式。 1. 饿汉式(静态常量) 饿汉式单例在类加载时就完成了初始化,因此线程安全。这种方式简单且效率高,但如果单例对象不被使用,会造成内存浪费。 ```java public class...
以下是几种常见的单例模式实现方式: 1. **饿汉式(静态常量)**: 这是最简单的实现方式,它在类加载时就完成了初始化,因此是线程安全的。 ```java public class Singleton { private static final Singleton...
单例模式的实现方式有很多种,下面我们将详细探讨几种常见的单例模式的实现方法: 1. 饿汉式(静态常量): 这种方式在类加载时就完成了初始化,因此是线程安全的。 ```java public class Singleton { private ...
在 Java 中,单例模式的写法有好几种,主要有懒汉式单例、饿汉式单例、登记式单例等。 懒汉式单例是一种常见的单例模式实现方式,它在第一次调用的时候实例化自己。下面是懒汉式单例的四种写法: 1、基本懒汉式...
在Java中,实现单例模式有多种方法,每种方法都有其特点和适用场景。以下是对这六种常见单例模式实现方式的详细解释: 1. **饿汉式(静态常量)** 这是最简单的单例实现,它在类加载时就创建了实例,线程安全。 `...
此外,单例模式还有几种变体,比如静态内部类单例和枚举单例。静态内部类单例利用Java类加载机制保证了线程安全,而枚举单例则是Java中实现单例的最佳方式,因为它天然支持序列化且防止反射攻击。 在代码实现上,...
下面我们将深入探讨单例模式的几种实现方式。 ### 1. 饿汉式(静态常量) 饿汉式单例在类加载时就完成了初始化,因此是线程安全的。代码如下: ```java public class Singleton { private static final ...
根据给定的信息,本文将详细解释几种常见的单例模式,并通过具体的Java代码示例来阐述每种模式的特点和实现方式。 ### 单例模式简介 单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局...
单例模式的应用非常广泛,特别是在以下几种场景中: - **资源管理**:比如数据库连接池、线程池等,这些资源非常宝贵,重复创建不仅浪费资源,还可能引发性能问题。 - **配置信息管理**:如系统配置类,通常只需要...
首先,实现C#单例模式通常有几种常见方法: 1. 饿汉式(静态常量): 这种方式在类加载时就完成了实例化,线程安全,但可能导致不必要的内存占用。 ```csharp public class Singleton { private static ...
为了解决懒汉式单例模式的性能问题,有几种优化策略: 1. **双检锁/双重校验锁(DCL,即 double-checked locking)** ```java public class Singleton { private volatile static Singleton instance; private ...
单例模式的实现方式有很多种,主要分为以下几种: 1. **饿汉单例模式**:在类加载时就完成初始化,因此静态变量`instance`会随着类的加载而被创建。这种方式线程安全,且因为单例在类加载时已经创建,所以不存在多...
本文介绍了几种在Python中实现单例模式的方法:使用类装饰器、元类以及 `__new__` 方法。每种方法都有其适用场景,可以根据具体需求选择最合适的方式。无论采用哪种方法,单例模式都能帮助我们在程序中有效地管理...
首先,让我们了解单例模式的几种常见实现方式: 1. 饿汉式(静态常量): 这是最简单的单例实现,它在类加载时就完成了初始化,因此是线程安全的。 ```java public class Singleton { private static final ...
在C++中实现单例模式,通常有几种常见的方法: 1. **懒汉式**:也称为延迟初始化,只有当第一次调用单例对象时才创建实例。这种做法避免了在程序启动时就创建不必要的对象。不过,如果多个线程同时尝试创建实例,...
通过对上述应用场景的分析,我们可以总结出单例模式适用的几种情况: - **资源共享:** 当需要频繁访问某个资源(如日志文件、配置文件等)时,采用单例模式可以减少资源操作所带来的性能损耗。 - **控制资源:** ...