设计模式系列:一、单例模式

文章介绍了单例模式的概念,包括其在数学和逻辑学中的定义,以及在Java中的应用。文中提供了三种Java实现单例模式的方法:双重检验锁、静态内部类和枚举类,并分析了各自的优缺点。此外,文章还通过JDK的Runtime类和Spring框架的Bean管理来举例说明单例模式的实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。

单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”

Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”

要点:

  • 1、单例类只能有一个实例;
  • 2、单例类必须自行创建这个实例;
  • 3、单例类必须自行向整个系统提供这个实例。

二、代码实现

2.1 双重检验锁方式

/**
 * 双重检验锁方式
 * @author xch
 * 2023/2/2 10:59
 */
public class Singleton {

    private static volatile Singleton instance;

    /**
     * 私有,防止其他类通过new创建
     */
    private Singleton() {}

    /**
     * 懒加载
     * 线程安全
     * @return Singleton
     */
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2.2 静态内部类方式

/**
 * 静态内部类方式
 * @author xch
 * 2023/2/2 10:59
 */
public class Singleton {
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    /**
     * 私有,防止其他类通过new创建
     */
    private Singleton() {}
    
    /**
     * 此方法通过调用内部静态类,才会实例化intance
     * 懒加载
     * 线程安全
     * 缺点:不能传参
     * @return Singleton
     */
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

2.3 枚举类方式

/**
 * 枚举类
 * @author xch
 * 2023/2/2 10:59
 */
public enum Singleton {
    /**
     * 绝对防止反序列化创建新对象
     * 饿汉式
     * 线程安全
     */
    INSTANCE
}

三、优缺点

3.1 优点

1、实例控制

单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。

2、灵活性

因为类控制了实例化过程,所以类可以灵活更改实例化过程。

3.2 缺点

1、开销

虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。

2、可能的开发混淆

使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

四、源码实例

4.1 JDK的Runtime类

/**
 * Every Java application has a single instance of class
 * <code>Runtime</code> that allows the application to interface with
 * the environment in which the application is running. The current
 * runtime can be obtained from the <code>getRuntime</code> method.
 * <p>
 * An application cannot create its own instance of this class.
 *
 * @author  unascribed
 * @see     java.lang.Runtime#getRuntime()
 * @since   JDK1.0
 */

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
}

4.2 Spring的Bean默认是单例,下面是Spring怎么获取单例Bean的代码

Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。

doGetBean方法调用getSingleton进行bean的创建。同样使用了双重检验锁方式!

/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

象话

打赏犹如太阳穴的枪口

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值