- 1. 单例模式概述
- 2. 单例模式的结构与实现
- 3. 单例模式的应用实例
- 4. 饿汉式单例与懒汉式单例
- 饿汉式单例类
- 懒汉式单例类与双重检查锁定
- 饿汉式单例类与懒汉式单例类的比较
- 使用静态内部类实现单例模式
- 5. 单例模式的优缺点与适用环境
“Github:https://github.com/nateshao/design-demo/tree/main/JavaDesignPatterns/08-singleton
1. 单例模式概述
如何保证一个类只有一个实例并且这个实例易于被访问?
- 全局变量:可以确保对象随时都可以被访问,但不能防止创建多个对象
- 让类自身负责创建和保存它的唯一实例,并保证不能创建其他实例,它还提供一个访问该实例的方法
单例模式的定义
“单例模式:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。
对象创建型模式
要点:
- 某个类只能有一个实例
- 必须自行创建这个实例
- 必须自行向整个系统提供这个实例
2. 单例模式的结构与实现
单例模式的结构
**单例模式只包含一个单例角色:**Singleton(单例)
单例模式的实现
- 私有构造函数
- 静态私有成员变量(自身类型)
- 静态公有的工厂方法
public class Singleton {
private static Singleton instance = null; //静态私有成员变量
//私有构造函数
private Singleton() {
}
//静态公有工厂方法,返回唯一实例
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
3. 单例模式的应用实例
实例说明:
某软件公司承接了一个服务器负载均衡(Load Balance)软件的开发工作,该软件运行在一台负载均衡服务器上,可以将并发访问和数据流量分发到服务器集群中的多台设备上进行并发处理,提高了系统的整体处理能力,缩短了响应时间。由于集群中的服务器需要动态删减,且客户端请求需要统一分发,因此需要确保负载均衡器的唯一性,只能有一个负载均衡器来负责服务器的管理和请求的分发,否则将会带来服务器状态的不一致以及请求分配冲突等问题。如何确保负载均衡器的唯一性是该软件成功的关键,试使用单例模式设计服务器负载均衡器。
实例类图
实例代码
- LoadBalancer:负载均衡器类,充当单例角色
- Client:客户端测试类
分析:
代码语言:javascript复制//判断服务器负载均衡器是否相同
if (balancer1 == balancer2 && balancer2 == balancer3 && balancer3 == balancer4) {
System.out.println("服务器负载均衡器具有唯一性!");
}
4. 饿汉式单例与懒汉式单例
饿汉式单例类
饿汉式单例类(Eager Singleton)
代码语言:javascript复制public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}
懒汉式单例类与双重检查锁定
懒汉式单例类(Lazy Singleton)
延迟加载
代码语言:javascript复制public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
多个线程同时访问将导致创建多个单例对象!怎么办?
代码语言:javascript复制public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
// 锁方法
synchronized public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
代码语言:javascript复制……
public static LazySingleton getInstance() {
if (instance == null) {
// 锁代码段
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
……
代码语言:javascript复制public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() { }
// Double-Check Locking 双重检查锁定
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}
饿汉式单例类与懒汉式单例类的比较
饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长
懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响
使用静态内部类实现单例模式
- Java语言中最好的实现方式
- Initialization on Demand Holder (IoDH): 使用静态内部类(static inner class)
//Initialization on Demand Holder
public class Singleton {
private Singleton() {
}
//静态内部类
private static class HolderClass {
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return HolderClass.instance;
}
public static void main(String args[]) {
Singleton s1, s2;
s1 = Singleton.getInstance();
s2 = Singleton.getInstance();
System.out.println(s1==s2);
}
}
5. 单例模式的优缺点与适用环境
模式优点
- 提供了对唯一实例的受控访问
- 可以节约系统资源,提高系统的性能
- 允许可变数目的实例(多例类)
模式缺点
- 扩展困难(缺少抽象层)
- 单例类的职责过重
- 由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失
模式适用环境
- 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例