1.模式定义
保证一个类只有一个实例,并且提供一个全局访问点
2.应用场景
重量级的对象,不需要多个实例,如线程池,数据库连接池。
2.1 源码中的应用
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory
3.实现方式
3.1 懒汉模式
延迟加载, 只有在真正使用的时候,才开始实例化。
1)线程安全问题
2)double check 加锁优化
3)编译器(JIT),CPU 有可能对指令进行重排序,导致使用到尚未初始化
的实例,可以通过添加volatile 关键字进行修饰,
对于volatile 修饰的字段,可以防止指令重排。
public class LazySingletonTest {
public static void main(String[] args) {
Thread thread = new Thread(()->{
LazySingleton s1 = LazySingleton.getInstance();
Thread thread2 = new Thread(()->{
LazySingleton s1 = LazySingleton.getInstance();
private volatile static LazySingleton singleton = null;
public static LazySingleton getInstance(){
synchronized (LazySingleton.class){
singleton = new LazySingleton();
3.2.饿汉模式
类加载的 初始化阶段就完成了 实例的初始化 。本质上就是借助于jvm
类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安
全(JVM以
同步
的形式来完成类加载的整个过程)。
类加载过程:
1,加载二进制数据到内存中, 生成对应的Class数据结构,
2,连接: a. 验证, b.准备(给类的静态成员变量赋
默认值
),c.解析
3,
初始化
: 给类的静态变量赋
初值
只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即
main函数所在类,直接进行new 操作,访问静态属性、访问静态方
法,用反射访问类,初始化一个类的子类等.)
private static HungerSingleton singleton = new HungerSingleton();
private HungerSingleton() {
public static HungerSingleton getInstance() {
3.3 静态内部类
1).本质上是利用类的加载机制来保证线程安全
2).只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一
种形式。
class InnerClassSingleton {
private InnerClassSingleton() {
private static class InnerClassHandle {
private static InnerClassSingleton singleton = new InnerClassSingleton();
public static InnerClassSingleton getInstance(){
return InnerClassHandle.singleton;
3.4 反射攻击实例
静态内部类防止反射破坏