(二十)单例模式

1.概述

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

2.示例

Singleton 类,定义一个 GetInstanc e操作,允许客户访问它的唯一实例。GetInstance 是一个静态方法,主要负责创建自己的唯一实例。
Singleton.java

public class Singleton {
    private static Singleton instance;
    // 构造方法让其 private,这就堵死了外界利用new创建此类实例的可能
    private Singleton() {}
    // 此方法是获得本类实例的唯一全局访问点
    public static Singleton getInstance() {
        // 若实例不存在,则 new 一个新实例,否则返回已有的实例
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

客户端代码
Client.java

public class Client {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        // 比较两次实例化后对象的结果是实例相同
        if (s1 == s2) {
            System.out.println("两个对象是相同的实例");
        }
    }
}

输出如下。

两个对象是相同的实例

Process finished with exit code 0

单例模式因为 Singleton 类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说就是对唯一实例的受控访问。

3.多线程时的单例

多线程的程序中,多个线程同时,注意是同时访问 Singleton 类,调用 GetInstance() 方法,会有可能造成创建多个实例的。

public class Singleton {
    private static Singleton instance;
    // 程序运行时创建一个静态的进程辅助对象
    private static final Object syncRoot = new Object();
    private Singleton() {}
    public static Singleton getInstance() {
        // 在同一个时刻加了锁的那部分程序只有一个线程可以进入
        synchronized (syncRoot) {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

加锁会影响性能。

4.双重锁定

不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为 Double-Check Locking(双重锁定)。

public class Singleton {
    private static Singleton instance;
    // 程序运行时创建一个静态的进程辅助对象
    private static final Object syncRoot = new Object();
    private Singleton() {}
    public static Singleton getInstance() {
        // 先判断实例是否存在,不存在再加锁处理
        if (instance == null) {
            synchronized (syncRoot) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

不推荐这种方式,低版本 jdk 甚至不能保证正确

5.优化

public class Singleton {
    // 程序运行时创建一个静态的进程辅助对象
    private static final Object syncRoot = new Object();
    private Singleton() {}

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

    public static Singleton getInstance() {
        // 先判断实例是否存在,不存在再加锁处理
        return SingletonHolder.instance;
    }
}
posted @ 2023-02-26 21:56  DaleLee  阅读(22)  评论(0)    收藏  举报