设计模式-单例模式

一、定义

单例模式算是比较简单的一种设计模式了,其核心思想就是确保一个类只有一个实例,并提供一个全局访问点。

具体点来说,就是我们把单例类设计成自己管理的一个单独实例,同时避免其他类创建单例实例,要想获取单例实例,通过单例类是唯一的方式。所谓的全局访问点,单例实例提供给其他类的获取实例的方法,也就是在这个方法中,保证了单例类只有一个实例。

二、使用场景

有一些对象其实我们只需要一个,比如:线程池、缓存、日志对象等等,这些对象只能有一个实例,如果有多个,会导致很多问题,比如程序异常、资源使用过量等。

三、实现

单例模式实现的核心就是将构造器私有化,这样一来只有单例自身才能创建对象。

线程不安全的实现

java 复制代码
public class SimpleSingleton {
    //单例实例
    private static SimpleSingleton instance;
    //获取单例实例,如果不存在就创建
    public static SimpleSingleton getInstance(){
        if(instance == null){
            instance = new SimpleSingleton();
        }
        return instance;
    }

    private SimpleSingleton(){
        System.out.println(Thread.currentThread().getId() + "," + System.currentTimeMillis() + ",创建了SimpleSingleton");
    }
}

这样实现起来也很简单,在一般情况下是没有问题的,但是在多线程的情况下,就会出现问题。

java 复制代码
public class SingletonTest {

    public static void main(String[] args) {
       testSimpleSingletonTask();
    }

    static void testSimpleSingletonTask(){
        ExecutorService pool = Executors.newFixedThreadPool(10);
        SimpleSingletonTask simpleSingletonTask = new SimpleSingletonTask();
        pool.execute(simpleSingletonTask);
        pool.execute(simpleSingletonTask);
        pool.execute(simpleSingletonTask);
        pool.execute(simpleSingletonTask);
        pool.execute(simpleSingletonTask);
        pool.execute(simpleSingletonTask);
        pool.shutdown();
    }
    static class SimpleSingletonTask implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getId() + "," + SimpleSingleton.getInstance());
        }
    }
}

输出结果:

可以看到,在多线程的情况下,每个线程都创建了一个单例实例,这肯定不是我们想要的结果,那为什么会这样呢?

其实如果对多线程编程有了解的话很容易看出来,单例类中的instance是一个共享变量,而getInstance中提供了对这个共享变量的修改,但修改是线程不安全的,很容易在多个线程执行这个方法的时候,都判断变量为空,然后都创建了一个实例赋值。

线程安全的单例模式

上述线程不安全的原因就是对共享变量instance修改时没有考虑多线程的情况,那最简单的解决方法就是直接给getInstancesynchronized,改为下面这样:

java 复制代码
public class Singleton {
    //共享变量加上volatile
    private volatile static Singleton instance;

    private Singleton(){
        System.out.println(Thread.currentThread().getId() + "," + System.currentTimeMillis() + ",创建了Singleton");
    }
    //直接加synchronized
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }

}

这样肯定可以解决问题,但实际上,我们只有在第一次创建实例的时候需要加同步锁,在之后的时间里,实例已经存在了,就没有必要加锁了,这样会影响效率。

所以就有了下面这样的解决方法,双重校验锁:

java 复制代码
public class Singleton {

    private volatile static Singleton instance;

    private Singleton(){
        System.out.println(Thread.currentThread().getId() + "," + System.currentTimeMillis() + ",创建了Singleton");
    }

    public static Singleton getInstance(){
        if(instance == null){
            //如果为空了,再去加锁创建实例
            synchronized(Singleton.class){
                //获取之后再判断一下,双重校验。原因是因为多线程的情况下,一个线程获取到了锁进来执行,其他的线程等待获取锁,释放锁后,其他的线程会陆续获取锁再次进来,所以获取锁之后还需要再判断一下是否已经创建了
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

}

我们用下面的代码测试:

java 复制代码
public class SingletonTest {

    public static void main(String[] args) {
        testSafeSingletonTask();
    }
    static void testSafeSingletonTask(){
        ExecutorService pool = Executors.newFixedThreadPool(10);
        SafeSingletonTask safeSingletonTask = new SafeSingletonTask();
        pool.execute(safeSingletonTask);
        pool.execute(safeSingletonTask);
        pool.execute(safeSingletonTask);
        pool.execute(safeSingletonTask);
        pool.execute(safeSingletonTask);
        pool.execute(safeSingletonTask);
        pool.shutdown();
    }

    static class SafeSingletonTask implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getId() + "," + Singleton.getInstance());
        }
    }

}

结果,只有一个线程创建了实例,其他线程获取到的都是这个实例

相关推荐
敲代码的 蜡笔小新1 小时前
【行为型之解释器模式】游戏开发实战——Unity动态公式解析与脚本系统的架构奥秘
unity·设计模式·游戏引擎·解释器模式
JANYI20181 小时前
嵌入式设计模式基础--C语言的继承封装与多态
java·c语言·设计模式
敲代码的 蜡笔小新5 小时前
【行为型之观察者模式】游戏开发实战——Unity事件驱动架构的核心实现策略
观察者模式·unity·设计模式·c#
琢磨先生David9 小时前
构建优雅对象的艺术:Java 建造者模式的架构解析与工程实践
java·设计模式·建造者模式
敲代码的 蜡笔小新16 小时前
【行为型之策略模式】游戏开发实战——Unity灵活算法架构的核心实现策略
unity·设计模式·c#·策略模式
_yingty_16 小时前
Java设计模式-策略模式(行为型)
java·设计模式·策略模式
炎芯随笔1 天前
【C++】【设计模式】生产者-消费者模型
开发语言·c++·设计模式
阿沁QWQ1 天前
单例模式的两种设计
开发语言·c++·单例模式
workflower1 天前
使用谱聚类将相似度矩阵分为2类
人工智能·深度学习·算法·机器学习·设计模式·软件工程·软件需求
枣伊吕波1 天前
第六节第二部分:抽象类的应用-模板方法设计模式
android·java·设计模式