单例模式

单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个全局访问点以供外部代码使用。

结构图

  • 定义一个私有成员变量instance;
  • 构造函数为private私有的;
  • 声明了一个名为getInstance的public公有静态方法,返回其唯一实例,供客户端Client使用。

单例核心代码(Java实现)

public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

客户端代码

class Client {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();
        if (singleton1 == singleton2) {
            System.out.println("两个对象是相同的实例");
        }
    }
}

// 输出:两个对象是相同的实例

多线程时的单例

多线程时,同时并发访问Singleton类,调用getInstance()方法,可能会创建多个实例。
通过给进程加锁,例如使用synchronized关键字,其作用是当一个线程没有退出之前,先锁住这段代码不被其他线程调用执行,以保证同一时间只有一个线程在执行此段代码。

public class Singleton {
    private static Singleton instance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

此种方式是解决了多线程的问题,这样使得每次调用都需要锁,影响性能。

双重锁定

通过使用volatile关键字,作用是当synchronized变量被初始化成Singleton时,多线程能够正确处理synchronized变量。

public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        // 检查实例是否存在,不存在,进行下一步
        if (instance == null) {
            // 防止多个线程同时进入创建实例
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

为什么在synchronized里面还要做一次instance实例的判断?

  • instance存在,直接返回
  • 当instance为null,两个线程同时调getInstance()方法时,他们都会通过第一重instance==null的判断。加锁之后,由于“锁”机制,只会有一个线程进入,另一个在外面等待。
  • 此时,如果没有第二重instance==null判断,第一个创建了实例,此时第一个线程结束,第二个线程从等待位置开始执行,直接执行创建新的实例逻辑。
  • 加了第二重判断后,第一个创建了实例,此时第一个线程结束,第二个线程从等待位置开始执行,重新判断instance==null,第一个线程已经创建实例,这时第二个线程才不会创建实例。

饿汉式和懒汉式

饿汉式写法

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

特点:

  • 静态初始化方式在类被加载时,就将自己实例化。
  • 提前占用系统资源。

懒汉式
特点:

  • 被引用时,才将自己实例化
  • 懒汉式面临多线程访问的安全性,需要双重锁定处理保证安全。
posted @ 2024-06-09 10:40  cavan丶keke  阅读(3)  评论(0编辑  收藏  举报