简介
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
- 某些类,如id生成器等,只能有一个,需要使用单例模式。
- 单例模式的要点:一个类只能有一个实例、类必须自行创建这个实例、类必须自行向系统提供这个实例。
结构和实现
- 单例模式只有一个单例类,在内部创建唯一实例,通过静态方法提供这个实例。private的构造方法可以防止单例类在外部被实例化。
- 单例结构。
- 单例模式实现方式有很多:饿汉式、懒汉式、静态内部类、枚举类。
饿汉式单例
- 类加载时创建对象,实现简单。
懒汉式单例
- 在第一次调用getInstance时实例化,称为延时加载。
- 可以使用 synchronized加锁方法 或者双重检查锁定来处理多线程问题。
- synchronized锁定对getInstance静态方法的调用,确保只有一个线程调用方法,但是每次调用该方法均要进行线程锁定判断,性能较低。
- 双重检查的原因:某一瞬间线程A和B均通过的第一重判断,A进入锁定的代码块,创建实例结束后,B进入锁定的代码块,需要第二重判断防止B再次创建实例。
- volatile关键字强制线程从主内存中取变量,而不是线程的本地内存,这样对该变量的改变线程之间是可见的。但是这也会屏蔽一些指令重排的优化。volatile关键字原理。
静态内部类单例
- 饿汉式单例一开始就会消耗系统的资源,懒汉式单例线程控制复杂、性能受限。
- 可以使用静态内部类储存创建的单例,第一次调用getInstance方法的时候加载内部类HolderClass,初始化其中的静态变量instance。
- 利用classloder的机制来保证初始化instance时只有一个线程。JVM在类初始化阶段会获取一个锁,这个锁可以同步多个线程对同一个类的初始化 。
- 静态内部类可以实现延迟加载、保证线程安全、不影响性能,但是受编程语言限制,某些语言不支持。
- 利用classloder的机制来保证初始化instance时只有一个线程。JVM在类初始化阶段会获取一个锁,这个锁可以同步多个线程对同一个类的初始化 。
枚举单例
- 使用enum类型储存单例,类似静态内部类,编程更加简单,可以实现延迟加载、保证线程安全、不影响性能,但是受编程语言限制。
实例
- 开发负载均衡软件,将并发访问的数据分发到多个服务器上进行处理。由于服务器需要动态增减,并且用户的请求需要统一分发,为了避免分配冲突问题,需要确保负载均衡器的唯一性。
优缺点和适用范围
- 优点:
- 控制唯一实例的访问。
- 节约资源。系统只存在一个该类的实例,对于需要频繁创建和销毁的类,可以提高性能。
- 控制类的数目。扩展单例模式,使一个类拥有指定数量的实例。
- 缺点:
- 无抽象层。扩展困难。
- 单例类职责过重。提供创建方法和业务方法,违反单一职责原则。
- 可能被回收。在提供自动垃圾回收的环境中,如果对象长时间不使用,会被回收,从而丢失对象的状态。
- 适用环境:
- 系统只需要一个实例。如业务需要或者资源消耗太大。
- 调用无状态。不需要通过单例对象在每次调用中传递数据,否则需要处理并发问题。
jdk中的应用
- java.lang.Runtime,封装了运行时环境,饿汉式单例。
public class Runtime {
private static Runtime currentRuntime = new Runtime();
private Runtime() {}
public static Runtime getRuntime() {
return currentRuntime;
}
}