老生常谈了,
最佳实践无非是 懒汉 线程 安全的 , 双重判断 的 单例模式
双重检查锁(Double-Checked Locking)
双重检查锁定是一种优化版的懒汉式单例模式,它通过减少加锁的次数来提高性能。在首次检查时,如果实例已经创建,则不进行加锁;否则,在第二次检查时才加锁。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
在这个实现中,volatile
关键字保证了实例的创建过程在多线程环境下的可见性,并且避免了在多线程环境下因指令重排而导致的问题。
juc 讲过这些 可见性关键字和加锁啥的,ojbk的,
黑马视频的文案和菜鸟教程一样的,不知道都从哪里抄的
单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
概要
单例模式(Singleton Pattern)
意图
确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
主要解决
频繁创建和销毁全局使用的类实例的问题。
何时使用
当需要控制实例数目,节省系统资源时。
如何解决
检查系统是否已经存在该单例,如果存在则返回该实例;如果不存在则创建一个新实例。
关键代码
构造函数是私有的。
应用实例
- 一个班级只有一个班主任。
- Windows 在多进程多线程环境下操作文件时,避免多个进程或线程同时操作一个文件,需要通过唯一实例进行处理。
- 设备管理器设计为单例模式,例如电脑有两台打印机,避免同时打印同一个文件。
优点
- 内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时(如管理学院首页页面缓存)。
- 避免资源的多重占用(如写文件操作)。
缺点
- 没有接口,不能继承。
- 与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心实例化方式。
使用场景
- 生成唯一序列号。
- WEB 中的计数器,避免每次刷新都在数据库中增加计数,先缓存起来。
- 创建消耗资源过多的对象,如 I/O 与数据库连接等。
注意事项
- 线程安全:
getInstance()
方法中需要使用同步锁synchronized (Singleton.class)
防止多线程同时进入造成实例被多次创建。 - 延迟初始化:实例在第一次调用
getInstance()
方法时创建。 - 序列化和反序列化:重写
readResolve
方法以确保反序列化时不会创建新的实例。 - 反射攻击:在构造函数中添加防护代码,防止通过反射创建新实例。
- 类加载器问题:注意复杂类加载环境可能导致的多个实例问题。
结构
单例模式包含以下几个主要角色:
- 单例类:包含单例实例的类,通常将构造函数声明为私有。
- 静态成员变量:用于存储单例实例的静态成员变量。
- 获取实例方法:静态方法,用于获取单例实例。
- 私有构造函数:防止外部直接实例化单例类。
- 线程安全处理:确保在多线程环境下单例实例的创建是安全的。
实现
我们将创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。
SingleObject 类提供了一个静态方法,供外界获取它的静态实例。SingletonPatternDemo 类使用 SingleObject 类来获取 SingleObject 对象。
跳着看就行了,废话太多,菜鸟教程都有
单例模式这个,过一遍就行,之前学过了,
很基础的东西,没啥好说的,
破坏单例模式
总结
破坏单例模式的方法多种多样,常见的包括:
- 通过反射访问私有构造器。
- 通过序列化与反序列化。
- 通过克隆。
- 在多线程环境中没有同步控制。
- 通过不同的类加载器。
为了防止单例模式被破坏,通常我们可以:
- 使用
readResolve
方法来避免反序列化破坏单例。 - 重写
clone()
方法或实现Cloneable
接口时抛出异常。 - 使用同步机制来保证懒汉式单例的线程安全。
如果你的单例类需要更强的安全性,考虑使用 enum
类型的单例模式,它天生是线程安全的并且不会被反射、序列化或克隆破坏。
单例模式这个, 快速过一遍就行,不需要全部看,
用到再看,快速过,
后边还有很多要看的,
sb视频。
单例模式,sb视频, 事无巨细,讲了全部种类单例模式的实现,还有单例模式的破坏,例子很垃圾,
对于单例模式,不需要去记忆和知道这么多实现细节,
只需要掌握 大概,单例模式有很多种,懒汉,恶汉,静态,枚举,双重确认啥的,线程安全,
还有破坏单例的过一眼即可
知道单例的目的是 节省资源就行。
对于大多数开发者来说,学习和应用单例模式并不需要记住过多的实现细节。掌握单例模式的核心概念和常见的几种实现方式就足够了。简而言之,单例模式的关键点在于:
-
目的是节省资源:确保某个类只有一个实例,避免多次创建,节省内存和系统资源。
-
常见的几种实现方式:
- 懒汉式(Lazy Initialization):实例在第一次使用时创建。
- 饿汉式(Eager Initialization):类加载时就创建实例,适合没有复杂初始化逻辑的场景。
- 静态内部类(Bill Pugh):结合懒加载和线程安全,推荐使用的方式。
- 双重检查锁(Double-Checked Locking):使用同步保证线程安全,避免性能损失。
- 枚举类型:推荐的实现方式,线程安全且防止反射攻击。
-
单例模式的破坏方式:
- 反射:反射可以访问私有构造函数,创建多个实例。
- 序列化和反序列化:反序列化时,可能会创建新的实例。
- 克隆:如果类实现了
Cloneable
接口,可以通过clone()
方法创建副本。 - 多线程问题:如果没有适当同步,懒汉式实现可能导致多次创建实例。
重点总结:
- 单例模式的目标:确保一个类有且只有一个实例,避免资源浪费。
- 常见实现方式:懒汉式、饿汉式、静态内部类、双重检查锁、枚举。
- 线程安全:确保在多线程环境下也能正确工作。
- 破坏方式:反射、序列化、克隆、线程问题。
不需要对每个细节过分纠结,理解这些常见的实现方式和破坏方式即可。