java设计模式:单例模式

本文详细介绍了单例模式的概念及其在Java中的五种实现方式:懒汉式(线程不安全)、双检锁懒汉式(线程安全)、饿汉式、静态内部类及枚举方式,并对比了它们的特点。

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

单例模式(SingleTon Pattern):创建单一对象,使得外部获取的当前类的对象始终是唯一的,单例模式是Java设计模式中最简单的一种模式。


单例模式一般使用在获取需要保持全局统一的对象的场景中,比世界上的某个国家、某个国家的主席、特定的某个人等都是唯一的,所以是单例的。

单例模式的实现方式有很多种,可根据不同的场景灵活使用

1、懒汉式(线程不安全的方式)

顾名思义,懒汉式就是实现了懒加载,在需要加载的时候才去加载获取对应单例

public class LazySingleTonUnSafe {
    private static LazySingleTonUnSafe singleTon = null;

    private LazySingleTonUnSafe() {

    }

    public static LazySingleTonUnSafe getInstance() {
        if(singleTon == null)
            singleTon = new LazySingleTonUnSafe();
        return singleTon;
    }
}

上面的方式之所以是非线程安全的,是因为,getInstance方法中,singleTon = new LazySingleTonUnSafe()表达式两边不是同时执行的,假如有两个线程进来,A线程进来时,singleTon是null,执行了实例化以及赋值的操作,这时候B线程也进来了,此时由于A线程还没有return,所以singleTon依然是null,于是又进行了一次实例化与赋值操作,这就导致了两个不同的对象产生。

2、双检锁懒汉式加载(线程安全)

public class LazySingleTonSafe {
    private static volatile LazySingleTonSafe singleTon = null;

    public LazySingleTonSafe() {
    }

    public static LazySingleTonSafe getInstance() {
        if (singleTon == null) {
            synchronized (LazySingleTonSafe.class) {
                if(singleTon == null)
                    singleTon = new LazySingleTonSafe();
            }
        }

        return singleTon;
    }
}

volatile关键字可以保证被修饰的共享变量(类的成员变量、类的静态成员变量)在并发操作时保持原子性、可见性与有序性,原子性指对基本数据类型的操作的变量的读取和赋值操作时原子性的,要么执行,要么不执行,不能中断;可见性指一个线程修改了某个变量的值,这个新值对其他线程也是立即可见的;有序性指禁止进行指令重排(java自身在运行代码时会进行指令重排)。

synchronized是互斥锁,这里因为是静态方法,所以使用的是静态锁


3、饿汉式

public class HungerSingleTon {
    private static HungerSingleTon singleTon = new HungerSingleTon();

    private HungerSingleTon() {

    }

    public static HungerSingleTon getInstance() {
        return singleTon;
    }
}

这种方式不能实现懒加载,是线程安全的,但是有内存浪费的情况。

4、静态内部类

public class SingleTon {
    private static class Inner{
        private static final SingleTon singleTon = new SingleTon();
    }

    private SingleTon(){}

    public static final SingleTon getIns() {
        return Inner.singleTon;
    }
}

静态内部类的方式是可以实现懒加载的,因为只有调用getIns方法时才会去实例化目标对象,而且这种方式也是线程安全的,只是有个问题,如果内部类中对象初始化失败,则该对象再也无法被创建

5、枚举方式

public enum  SingleTonEnum{
    SINGLETON;

    private SingleTon singleTon = null;

    SingleTonEnum() {
        singleTon = new SingleTon();
    }

    public SingleTon getSingleTon() {
        return singleTon;
    }
}

这种方式不常用,但是确实是种很好的方式,枚举是实现单例最好的方式,使用

SingleTonEnum.SINGLETON.getSingleTon()

即可获取目标对象的实例,这个实例一定是单例的。


spring框架中的Bean实例默认也是单例的,下面是一段源码

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 != NULL_OBJECT ? singletonObject : null);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值