单例模式
描述
一个类全局只有一个实例,自行实例化并提供给使用。
构造函数私有化是前提
基本使用
防破坏单例
- 防反射:在构造函数 中判断类中实例是否已初始化
java
private InnerClassSingleton (){
if(InnerClassSingletonHolder.instance != null){
throw new RuntimeException("单例模式 不允许 多例存在");
}
}
- 防克隆:单例类不要实现Cloneable接口或者强制覆盖clone方法
如果采用的是强制覆盖clone方法,可以返回单例对象或者 直接报错 - 防序列化:对象反序列化会调用对象的readResolve方法,可以在类中声明这个方法,并返回单例对象
java
Object readResolve() throws ObjectStreamException{
return InnerClassSingletonHolder.instance;
}
饿汉式
- 静态成员初始化
java
public class HungrySingleton {
private static HungrySingleton instange = new HungrySingleton();
private HungrySingleton() {
if (instange != null) {
throw new RuntimeException("单例模式不允许 多例存在");
}
}
public static HungrySingleton getInstance() {
return instange;
}
}
- 静态代码块初始化
java
public class HungrySingleton {
private static HungrySingleton instange;
static {
instange = new HungrySingleton();
}
private HungrySingleton() {
if (instange != null) {
throw new RuntimeException("单例模式不允许 多例存在");
}
}
public static HungrySingleton getInstance() {
return instange;
}
}
- 枚举(直接用吧JVM层面保证单例)
java
/**
* @author sdaf
* <p>
* 枚举:本质也是一个类,底层是一个实现 Eunm 的一个类,编译器会默认添加一些属性及方法
* 枚举站在单例实现角度上看 就是一个饿汉模式实现
*/
public enum EnumSingleton {
INSTANGE;
public void test(){
// code
}
}
懒汉式
- synchronized + double check
java
public class LazySingleton {
private static volatile LazySingleton lazySingleton;
private LazySingleton() {
if(lazySingleton!=null){
throw new RuntimeException("单例模式不允许 多例存在");
}
}
public static LazySingleton getInstance() {
if (lazySingleton == null) {
// 线程A 线程B 均进入非空判断逻辑
// 线程A 先行添加锁,然后创建实例对象,并返回
// 此时线程B已经 进入非空的逻辑,然后依然会去加锁,然后创建对象,此时就违背了单例对象定义
// 所以需要在加锁的内部再次进行非空判断,保证对象单例
synchronized (LazySingleton.class) {
if (lazySingleton == null) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
lazySingleton = new LazySingleton();
// new 一个对象步骤进一步解析
// 1、在堆内存开辟一块空间
// 2、初始化对象(创建对象)
// 3、将开辟的内存空间地址赋值给变量
// 【但是】由于编译器和CPU的指令重排可能导致 2 和 3 执行顺序发生改变
// 为 1 3 2
// 此时如果第二个线程 在 第一个线程执行到 3 与 2之间时 进来
// instance 是有值的,因为 instance 拿到的只是 内存地址而已,并不知道这个地址上是否实在的对象
// 只有当这个对象完成初始化(创建)后这个地址上才有具体的值
// 此时第二个线程拿到地址就会直接返回这个引用,
// 但是此时这个对象是个 null , 可能会引起 NullPointerException
// 为了防止 整个 new 操作被执行重排 需要使用关键字 volatile 来修饰变量
}
}
}
return lazySingleton;
}
- 静态内部类
java
/**
* @author sdaf
* @Description 基于静态内部类的单例模式,也是一种懒加载
* 也是借助于JVM类加载机制实现的线程安全
* 类加载顺序:
* 1.加载类的Class文件到内存中
* 2.连接JVM,验证Class合法性,静态成员赋默认值,解析处理后的数据
* 3.给静态成员赋值
* 静态内部类只有在使用这个类的时候才会去加载这个类
*/
public class InnerClassSingleton implements Serializable{
private static final long serialVersionUID = -790330830636259886L;
private static class InnerClassSingletonHolder{
private static InnerClassSingleton instance= new InnerClassSingleton();
}
private InnerClassSingleton (){
if(InnerClassSingletonHolder.instance != null){
throw new RuntimeException("单例模式 不允许 多例存在");
}
}
public static InnerClassSingleton getInstance(){
return InnerClassSingletonHolder.instance;
}
//
Object readResolve() throws ObjectStreamException{
return InnerClassSingletonHolder.instance;
}
}
有上限多例
单例模式可以扩展为有上限多例
java
public class Multiton {
private Multiton() {}
private static final int MAX_INSTANCES = 3;
private static final List<Multiton> instances = new ArrayList<>();
// 饿汉式初始化
static {
for (int i = 0; i < MAX_INSTANCES; i++) {
instances.add(new Multiton());
}
}
// 懒汉式初始化
public static Multiton getInstanceLazy(int index) {
if (instances.get(index) == null) {
synchronized (Multiton.class) {
if (instances.get(index) == null) {
instances.set(index, new Multiton());
}
}
}
return instances.get(index);
}
// 随机获取
public static Multiton getRandomInstance() {
Random random = new Random();
return instances.get(random.nextInt(MAX_INSTANCES));
}
// 指定获取
public static Multiton getInstance(int index) {
if (index < 0 || index >= MAX_INSTANCES) {
throw new IllegalArgumentException("索引超出范围");
}
return instances.get(index);
}
// 业务方法
public void test() {
}
}