Liuqichun's Blog

设计模式|单例模式

在软件开发过程中经常会遇到一个类只需要一个对象,我们在整个程序的运行过程中仅使用这个对象完成一部分功能,这种类的设计有一个传统的名字——单例模式。单例模式体现了功能的高内聚,单例独自拥有系统的一部分功能,通过单例对象在系统的任何地方调用方法,也就统一管理了系统的这部分功能。

单例在前后端都用得很多。从前端来说,JS 中的 window 对象、document 对象都是某个网页的单例,它们集中管理着网页窗口和网页功能的 API,Vue 中的 Vuex 也是单例的;从后端来说,连接池对象可以是单例的,网站计数器也是单例的。

下面介绍三种经典的单例。

饿汉单例

饿汉单例的对象在类被加载的时候就会被初始化好,这样能避免多线程时多次创建对象的问题。不好的一点是,无论用不用这个类(只要一 import)它就初始化了,因此相比于懒汉单例,在导入却不使用时会浪费存储空间。

 1public class Singleton {
 2    private static Singleton instance = new Singleton();
 3
 4    private Singleton() {
 5        System.out.println("Singleton instance has init.");
 6    }
 7
 8    public static Singleton getInstance(){
 9        return instance;
10    }
11
12    public void find(){
13        //do operate
14        System.out.println("you do a find operate.");
15    }
16}

懒汉单例

一 懒汉单例在第一次使用时才进行初始化。这种初始化在多线程环境下会多创建对象,这是不符合单例模式的逻辑的(虽然在之后多次调用返回同一个对象),解决方法是加锁。 在这里插入图片描述

 1/**
 2 * 单例-懒汉
 3 * getInstance() 方法在多线程环境下可能会出现线程安全问题
 4 */
 5public class Singleton {
 6    private static Singleton instance;
 7
 8    private Singleton() {
 9        System.out.println("Singleton instance has init.");
10    }
11
12    /**
13     * 线程不安全
14     */
15    public static Singleton getInstance() {
16        if(null == instance){
17            instance = new Singleton();
18        }
19        return instance;
20    }
21
22    public void find() {
23        //do operate
24        System.out.println("you do a find operate.");
25    }
26}

二

对方法加锁,采用双 if 判断保障仅初始化一个对象。如果用一个 if 仍不能保证只创建一个对象,这时需要双 if 判断。当多条线程同时进入 if 语句,一个线程拿到锁,其他线程进入阻塞状态;如果在锁中不加以判断 instance 是否为空,其他线程拿到锁仍会创建对象。

 1/**
 2 * 单例-懒汉 线程安全
 3 */
 4public class Singleton2 {
 5    private static Singleton2 instance;
 6    private static int count = 0;
 7    private static Object lock = new Object();
 8
 9    private Singleton2() {
10        System.out.println("Singleton2 instance has init.---" + (++count));
11    }
12
13    /**
14     * 加锁, 双重if, 保证只初始化一个对象
15     */
16    public static Singleton2 getInstance() {
17        if (instance == null) {
18            synchronized (lock) {
19                if(instance==null){
20                    instance = new Singleton2();
21                }
22            }
23        }
24        return instance;
25    }
26
27    public void find() {
28        //do operate
29        System.out.println("you do a find operate.");
30    }
31}

枚举单例

枚举是通过 JVM 的机制来保证使用的时候对象已被初始化且只有一个对象,这个可以确保在多线程环境下只初始化一个对象。这种单例是《Effective Java》所提倡的。

 1public enum EnumSingleton {
 2    instance();
 3
 4    EnumSingleton(){
 5        System.out.println("EnumSingleton has init.");
 6    }
 7
 8    public void find() {
 9        //do operate
10        System.out.println("you do a find operate.");
11    }
12}

总结

简单总结下使用单例的情况:

  1. 当类只能有一个实例,而且第三方可以从一个公共的访问点访问它时
  2. 当一个唯一的实例可以通过子类化来扩展,而且第三方要在不更改代码的情况下就能使用一个扩展的实例时

<< Previous Post

|

Next Post >>

#设计模式