设计模式-单例

设计模式-单例模式

一、概述

​ 单例模式在框架中是比较常见的,比如大名鼎鼎的Spring框架中,会默认把bean设计成单例。那Spring集成Spring MVC、Struts2等也都是单例的,因为只要把创建对象的权利交给Spring来管理后,那么就可以通过注入的方式来获取实例对象。

​ 单例的概念:创建类在当前进程中只有一个实例。简单来说就是一个类只有一个实例。

二、编写单例模式代码

​ 编写单例模式三大步骤:

1.构造函数私有化
2.类的内部创建实例
3.对外提供方法
1.饿汉式
public class Singleton{
    private statice singleton = new Singleton();
    
    private Singleton(){
        
    }
    public Singleton getInstence() {
        return singleton;
    }
    
}

​ 饿汉式基本上是按照三大步骤来写出来的,但是我们仔细观察发现会有瑕疵。就是不管我们有没有调用getInstence()方法,这个类都被new出来了。我们知道new 一个对象会在堆中开辟空间,那堆属于jvm内存模型的一类,那显然是要消耗内存的。我们第一反应应该是根据我们学习的懒加载来实现进一步优化。所谓懒加载也就是在方法调用的时候再创建对象。

2.懒汉式
public class Singleton{
    private statice singleton ;
    
    private Singleton(){
        
    }
    public Singleton getInstence() {
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
    
}

​ 这样就做到了一个简单优化,所以名字也就不一样了。此时我们叫做懒汉式。我们再仔细想想,懒汉式有什么缺点?

public class TestSingleton implements Callable<String> {

    @Override
    public String call() throws Exception {
        Singleton03 singleton03 = Singleton03.getSingleton03();
        return singleton03.toString();
    }

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        List<Future<String>> callableList = new ArrayList<Future<String>>();
        for (int i = 0; i < 100; i++) {
            Future<String> submit = executorService.submit(new TestSingleton());
            callableList.add(submit);
        }

        try {
            for (Future<String> future : callableList) {
                System.out.println(future.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

结果:

singleton.Singleton03@7f82d215
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@478899a9
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d

​ 根据结果观察:我们的单例模式还是有瑕疵。在多线程环境下,我调用getSingleton03()方法,但是明显打印出来的地址是有不一样的。也就是我们我们在多线程下创建100次对象,无法保证每次都是同一个实例。我们可以想想其中一种情况,当我们线程1走到判断singleton是不是为空的时候,线程2正准备创建实例了,那线程1判断这个singleton还是为null,那我也会创建一个实例。所以创建出来的实例相当于new了两次。我们可以得知,多线程下这种简单的懒汉式是有瑕疵的,是无法保证线程安全的。我们应该继续对单例进行优化。

​ 怎么优化呢?我们首先想到synchronized关键字。

public class Singleton {
    //声明变量
    private static Singleton singleton = null;

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

    }

    //对外提供方法
    public static synchronized Singleton getSingleton(){
        if (singleton == null){
        	singleton = new Singleton();
        }
        return singleton;
    }
}

​ 那我们来测试验证一下结果:

singleton.Singleton03@7f82d215
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@b16e737
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d
singleton.Singleton03@6334f86d

​ 结果显示,加上synchronized结果并不是那么如人意。此时我们意识到,给整个方法加锁不仅导致无法保证线程安全,而且效率还低下。

public class Singleton {
    //声明变量
    private static volatile Singleton02 singleton = null;

    //私有化构造参数
    private Singleton02(){

    }

    //对外提供方法
    public static Singleton getSingleton(){
        if (singleton == null){
            synchronized (Singleton.class){
                if (singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

​ 单例的双锁机制,我们会思考一下为什么需要volatile关键字。首先我们想一下volatile关键字作用有哪些:

1.保证线程之间可见性。一个线程修改变量,可以强制刷新到主内存,保证线程和主线程之间的变量是一致的。
2.禁止指令重排。什么叫指令重排序,由于机器的自身优化功能,有些程序代码会优先执行,也就是计算机指令会进行一个优先级排序,再按照这个顺序执行,很有可能导致程序出错。

​ 在单例设计模式中,我们可以看到volatile的作用是第二层:即禁止指令重排。除了上面这些,当然还有其他两种单例模式:静态内部类懒汉式、枚举。我们可以一一来学习一遍。

3.静态内部类懒汉式
public class Singleton {

    private Singleton() {

    }
    // 创建静态内部类
    private static class InnerClass {
        private static final Singleton singleton = new Singleton();
    }

    // 对外提供方法
    public static final Singleton getSingleton() {
        return InnerClass.singleton;
    }
}

4.枚举
public enum Singleton {
    Singleton;
    public  Singleton getSingleton() {
        return Singleton;
    }
}
posted @ 2020-03-19 17:27  像他的尘公子  阅读(87)  评论(0)    收藏  举报