单例模式详解

本文深入探讨了单例模式的概念及其实现方式,包括懒汉模式和饿汉模式,并对比了这两种模式的不同之处。同时,文章还介绍了单例模式在多线程环境下的问题及其解决方法,并比较了单例模式与Spring容器单例的区别。
摘要由CSDN通过智能技术生成

单例模式:一般是指在运行的JVM中只有一个实例存在。通过使用 private的构造函数确保了在一个应用中只能产生一个类的实例,

并且实例是在单例类Singleton中自己实例化的。

主要分为 懒汉模式和饿汉模式

两者结构:

均是 一个静态的实例[ 静态为类对象]

私有的构造方法。防止外部直接常见对象

静态方法:返回类中的静态实例对象


懒汉,饿汉主要是创建实例的时机不一样。

饿汉模式:是在类加载后就会对类的静态对象进行初始化。

private static final Singleton singleton = new Singleton();

//私有化构造函数
private Singleton() {
}

//给出一个公共的静态方法返回一个单一实例
public static Singleton getInstance() {
    return singleton;
}

这个方法却牺牲了Lazy的特性。单例类加载的时候就实例化了。如注释所述:非懒加载,如果构造的单例很大,构造完又迟迟不使用,会导致资源浪费。

懒汉模式:是在方法调用的时候才会创建实例。

private static Singleton singleton;

//私有化构造函数
private Singleton() {
}

//给出一个公共的静态方法返回一个单一实例
public static Singleton getInstance() {
    if (singleton == null) {
        singleton = new Singleton();
    }
    return singleton;
}




单例模式推荐的写法

private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
}

private Singleton() {
}

public static Singleton getInstance() {
    return SingletonHolder.INSTANCE;
}


static的成员变量只在类加载的时候初始化一次,且类加载是线程安全的。所以这个方法实现的单例是线程安全的。

内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且类的加载过程是线程安全的。内部类加载的时候实例化一次instance。全局也就一个单例


模拟多线程环境下。懒汉单例模式的缺陷,可能导致在jvm中产生多个实例。


public static void main(String[] args) throws Exception {
    int num = 20;
    final CyclicBarrier start = new CyclicBarrier(num);
    final CountDownLatch end = new CountDownLatch(num);
    final Set<String> set = Collections.synchronizedSet(new HashSet<String>());
    ExecutorService executorService = Executors.newFixedThreadPool(num);
    for (int i = 0; i < num; i++) {
        executorService.execute(new SingletonRunnable(i, start, end, set));
    }
    end.await();
    System.out.println("------并发情况下我们取到的实例------");
    for (String instance : set) {
        System.out.println(instance);
    }
    executorService.shutdown();

}


public static class SingletonRunnable implements Runnable {

    private CountDownLatch countDownLatch;
    private CyclicBarrier start;
    private Integer id;
    private Set<String> set;

    SingletonRunnable(Integer id, CyclicBarrier start, CountDownLatch countDownLatch, Set<String> set) {
        this.countDownLatch = countDownLatch;
        this.start = start;
        this.id = id;
        this.set = set;
    }

    @Override
    public void run() {
        try {
            System.out.println("准备构造------------" + id);
            start.await();
            System.out.println("开始构造" + id);
            set.add(Singleton.getInstance().toString());
        } catch (Exception e) {

        }
        countDownLatch.countDown();
    }
}



与Spring容器的单例的区别:

一般的单例是指JVM中只有一个实例, spring 的单例是spirng 容器中只有一个实例。

Spring中说的单例是相对于容器的,既在ApplicationContext中是单例的。而平常说的单例是相对于JVM的。另一个JVM可以有多个Spring容器,而且Spring中的单例也只是按bean的id来区分的



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值