定义
单例模式确保一个类只有一个实例,并提供一个全局访问点。
- 我们把某个类设计成自己管理的一个单独实例,同时也避免其他类再自行产生实例。要想取得单例模式,通过单例类是唯一的途径。
- 我们也提供对这个实例的全局访问点:当你需要实例时,向类查询,它会返回单个实例。
结构图

实现
使用一个私有构造函数,一个私有静态变量以及一个公有静态函数来实现。
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。
饿汉式-线程不安全
如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切创建此单件,如下所示:
1 | public class Singleton { |
利用这个做法,我们依赖JVM在加载这个类时马上创建此唯一的单件实例,JVM保证在任何线程访问uniqueInstance静态变量之前,一定先创建此实例。
懒汉式-线程不安全
以下实现中,私有静态变量uniqueInstance被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化uniqueInstance,从而节省资源。
1 | public class Singleton { |
这个实现在多线程环境下是不安全的,如果多个线程能够同时进入if(uniqueInstance == null),那么会有多个线程执行uniqueInstance = new Singleton();语句,这将会导致实例化多次uniqueInstance。
懒汉式-线程安全
只要接着上面的,把getInstance变成同步(Synchronized)方法,多线程灾难几乎就可以轻易地解决。
1 | public class Singleton { |
这样虽然可以解决问题,但是同步会降低性能,而且比你想象的还要严重一些的是:只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步都是一种累赘。
双重检查加锁-线程安全
利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,才进行同步,这样一来,只有第一次会同步,这正是我们想要的。
1 | public class Singleton { |
volatile关键词确保:当uniqueInstance变量被初始化成Singleton实例时,多个线程正确的处理uniqueInstance变量。
静态内部类实现
当Singleton类被加载时,静态内部类SingletonHolder没有被加载进内存,只有当调用getInstance方法从而触发SingletonHolder.uniqueInstance时,SingletonHolder才会被加载,此时初始化uniqueInstance实例,并且JVM能确保uniqueInstance只被实例化一次。
1 | public class Singleton { |
这种方式不仅具有延迟初始化的好处,而且由JVM提供了对线程安全的支持。
枚举类实现
1 | public enum Singleton { |
该实现可以防止反射攻击。在其它实现中,通过setAccessible()方法可以将私有构造函数的访问级别设置为public,然后调用构造函数从而实例化对象。如果想要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由JVM保证只会实例化一次,因此不会出现上述的反射攻击。
该实现在多次序列化和反序列化之后,不会得到多个实例,而其它实现需要使用transient修饰所有字段,并且实现序列化和反序列化的方法。
总结
- 单例模式确保程序中一个类最多只有一个实例。
- 单例模式也提供访问这个实例的全局点。
- 在Java中实现单例模式需要私有的构造器,一个静态方法和一个静态变量。
- 确定在性能和资源上的限制,然后小心的选择适当的方案来实现单例,以解决多线程的问题。
- 小心你如果使用多个类加载器,可能导致单例失效而产生多个实例。