推荐链接:
总结——》【Java】
总结——》【Mysql】
总结——》【Spring】
总结——》【SpringBoot】
单例模式的核心是保证一个类只有一个实例,并且提供一个访问实例的全局访问点。
实现方式 | 优缺点 |
---|---|
饿汉式 | 线程安全,调用效率高 ,但是不能延迟加载,可能造成空间资源浪费 |
懒汉式:锁式 | 线程安全,调用效率不高synchronized,能延迟加载 |
懒汉式:双重检测锁式 | 在懒汉式的基础上解决并发问题 |
静态内部类式 | 线程安全,资源利用率高,可以延时加载 |
枚举单例 | 线程安全,调用效率高,但是不能延迟加载 |
/**
* 单例模式:饿汉式
*/
public class SingletonForHungry {
// 缺点:如果对象没有被使用,造成空间资源浪费
private byte[] b1 = new byte[1024 * 1024];
private byte[] b2 = new byte[1024 * 1024];
private byte[] b3 = new byte[1024 * 1024];
// 声明此类型的变量,并实例化,当该类被加载的时候就完成了实例化并保存在了内存中
private static final SingletonForHungry singletonInstance = new SingletonForHungry();
// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonForHungry() {
}
// 对外提供一个获取实例的静态方法
public static SingletonForHungry getSingletonInstance() {
return singletonInstance;
}
public static void main(String[] args) {
System.out.println(SingletonForHungry.singletonInstance);
System.out.println(SingletonForHungry.singletonInstance);
}
}
/**
* 单例模式:懒汉式(锁式)
*/
public class SingletonForLazySynchronized {
// 声明此类型的变量,但没有实例化
private static SingletonForLazySynchronized singletonInstance;
// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonForLazySynchronized() {
}
// 对外提供一个获取实例的静态方法,为了数据安全添加synchronized关键字
public static SingletonForLazySynchronized getSingletonInstance() {
if (singletonInstance == null) {
singletonInstance = new SingletonForLazySynchronized();
}
return singletonInstance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(SingletonForLazySynchronized.getSingletonInstance());
}).start();
}
}
}
/**
* 单例模式:懒汉式(双重检测锁式)
*/
public class SingletonForLazyDoubleCheck {
// 声明此类型的变量,但没有实例化
// 必须加volatile,防止指令重排序
private static volatile SingletonForLazyDoubleCheck singletonInstance;
// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonForLazyDoubleCheck() {
}
// 对外提供一个获取实例的静态方法
public static SingletonForLazyDoubleCheck getSingletonInstance() {
if (singletonInstance == null) {
synchronized (SingletonForLazyDoubleCheck.class) {
// 双重检测
if (singletonInstance == null) {
// 加volatile防止指令重排序
// 1.分配内存空间
// 2.创建实例对象
// 3.把这个内存地址赋值给变量的引用
singletonInstance = new SingletonForLazyDoubleCheck();
}
}
}
return singletonInstance;
}
public static void main(String[] args) throws Exception {
System.out.println(DoubleCheckSingleton.getSingletonInstance());
System.out.println(DoubleCheckSingleton.getSingletonInstance());
}
}
/**
* 单例模式:静态内部类
*/
public class SingletonForStaticInner {
// 静态内部类
public static class SingletonClassInstance {
// 声明外部类型的静态常量
public static final SingletonForStaticInner singletonInstance = new SingletonForStaticInner();
}
// 私有化构造方法
private SingletonForStaticInner() {
}
// 对外提供的唯一获取实例的方法
public static SingletonForStaticInner getSinletonInstance() {
return SingletonClassInstance.singletonInstance;
}
public static void main(String[] args) {
System.out.println(SingletonForStaticInner.getSinletonInstance());
System.out.println(SingletonForStaticInner.getSinletonInstance());
}
}
/**
* 单例模式:枚举
*/
public enum SingletonForEnum {
INSTANCE;
public static void main(String[] args) {
System.out.println(SingletonForEnum.INSTANCE);
System.out.println(SingletonForEnum.INSTANCE);
}
}
Q:为什么反射可以破坏单例模式?
A:因为反射可以操作私有的属性和方法,通过私有的构造器来创建实例。
Q:可以用反射创建枚举对象吗?
A:不可以,源码中有判断。
package com.example.test.singleton;
import java.lang.reflect.Constructor;
/**
* 单例模式:懒汉式(双重检测锁式)
*/
public class SingletonForLazyDoubleCheck {
// 声明此类型的变量,但没有实例化
// 必须加volatile,防止指令重排序
private static volatile SingletonForLazyDoubleCheck singletonInstance;
// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonForLazyDoubleCheck() {
}
// 对外提供一个获取实例的静态方法
public static SingletonForLazyDoubleCheck getSingletonInstance() {
if (singletonInstance == null) {
synchronized (SingletonForLazyDoubleCheck.class) {
// 双重检测
if (singletonInstance == null) {
// 加volatile防止指令重排序
// 1.分配内存空间
// 2.创建实例对象
// 3.把这个内存地址赋值给变量的引用
singletonInstance = new SingletonForLazyDoubleCheck();
}
}
}
return singletonInstance;
}
public static void destorySigleton() throws Exception {
// 反射可以暴力破解
Class<SingletonForLazyDoubleCheck> cls = SingletonForLazyDoubleCheck.class;
SingletonForLazyDoubleCheck singleton = cls.newInstance();
System.out.println("反射获取实例对象 = " + singleton);
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
declaredConstructors = cls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(null);
System.out.println("反射获取实例对象 = " + o);
}
}
public static void main(String[] args) throws Exception {
destorySigleton();
}
}
解决的方式是在无参构造方法中手动抛出异常控制,或者声明一个全局变量来控制。
package com.example.test.singleton;
import java.lang.reflect.Constructor;
/**
* 单例模式:懒汉式(双重检测锁式)
*/
public class SingletonForLazyDoubleCheck {
// 声明此类型的变量,但没有实例化
// 必须加volatile,防止指令重排序
private static volatile SingletonForLazyDoubleCheck singletonInstance;
private static boolean flag = false; // false 表示对象还没有被创建
// 私有化所有的构造方法,防止直接通过new关键字实例化
private SingletonForLazyDoubleCheck() {
if(!flag){
// 说明是第一次创建
flag = true;
singletonInstance = this;
}else{
throw new RuntimeException("已经有了对象,就不要再创建了...");
}
}
// 对外提供一个获取实例的静态方法
public static SingletonForLazyDoubleCheck getSingletonInstance() {
if (singletonInstance == null) {
synchronized (SingletonForLazyDoubleCheck.class) {
// 双重检测
if (singletonInstance == null) {
// 加volatile防止指令重排序
// 1.分配内存空间
// 2.创建实例对象
// 3.把这个内存地址赋值给变量的引用
singletonInstance = new SingletonForLazyDoubleCheck();
}
}
}
return singletonInstance;
}
public static void destorySigleton() throws Exception {
// 反射可以暴力破解
Class<SingletonForLazyDoubleCheck> cls = SingletonForLazyDoubleCheck.class;
SingletonForLazyDoubleCheck singleton = cls.newInstance();
System.out.println("反射获取实例对象 = " + singleton);
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
declaredConstructors = cls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(null);
System.out.println("反射获取实例对象 = " + o);
}
}
public static void main(String[] args) throws Exception {
destorySigleton();
}
}
// 重写该方法,防止序列化和反序列化获取实例
private Object readResolve() throws ObjectStreamException {
return singletonInstance;
}
1、Spring中的Bean对象(默认是单例模式)
2、相关的工厂对象,比如:MyBatis中的SqlSessionFactory,Spring中的BeanFactory
3、相关的配置对象,比如:MyBatis中的Configuration对象,SpringBoot中的各个XXAutoConfiguration对象
4、应用程序的日志框架
5、数据库连接池