【JavaEE】单例模式

时间:2022-09-20 01:19:25

为什么要有单例模式

在一个项目里,可能有一些对象是专门用来保存一些数据的,这个对象可能非常大。
可能占用内存几十个G,或者几百G,或者上千个G。
【JavaEE】单例模式

如果这个对象不小心再被创建一次,服务器的内存可能就被占用光了。
【JavaEE】单例模式
所以说,保证这个对象只有一个是一件非常重要的事。

饿汉和懒汉的意思

这里的饿汉和懒汉跟我们平时听到的并不是同一个意思。

饿汉表示急切急迫的意思。

懒汉表示不急迫从容的意思。

饿汉式实现

这种实现中是先把对象准备好的(比较急迫),所以就叫成饿汉式(Eager)。

  1. 先创建对象
  2. 将构造器私有化,防止类外创建对象
  3. 暴露一个获取对象的方法
public class SingletonEager {
    private static SingletonEager instance = new SingletonEager();

    private SingletonEager(){

    };

    public static SingletonEager getInstance() {
        return instance;
    }
}
  • 在多线程的情况下,饿汉式实现不会涉及到变量的修改操作,所以是线程安全的。

饿汉式测试:

public class Test {
    public static void main(String[] args) {
        SingletonEager instance = SingletonEager.getInstance();
        SingletonEager instance2 = SingletonEager.getInstance();
        System.out.println(instance == instance2);
    }
}

输出:
【JavaEE】单例模式

懒汉式实现

懒汉式(Lazy)跟饿汉式不同,当调用获取对象的方法时,才会创建一个对象。

代码:

public class SingletonLazy {
    public static SingletonLazy obj = null;

    private SingletonLazy(){

    };

    public static SingletonLazy getInstance() {
        if (obj == null) {
            obj = new SingletonLazy();
        }
        return obj;
    }

}

但是这不是线程安全的,因为当多个线程同时调用getInstance方法的时候,就有可能创建无数多个对象。
下图以两个线程为例子:
【JavaEE】单例模式

  • 上图就表示了,多个线程执行这段代码的时候就有可能创建多个对象,所以要上锁。在这里要对整个if代码块上锁。

代码如下:

public static SingletonLazy getInstance() {
        synchronized(lock) {
            if (obj == null) {
                obj = new SingletonLazy();
            }
        }
        return obj;
    }
  • 但是呢,如果有很多线程同时调用这个方法,而且当obj != null 时,对于上面的代码会导致很多线程都阻塞了。
  • 然而obj != null时并不会执行创建对象的操作,只执行return obj,此时线程是安全的,所以再进行一次判断。
  • 只有当obj == null的时候才执行synchronized(lock){....}

代码:

public static SingletonLazy getInstance() {
        if (obj == null) {
            synchronized(lock) {
                if (obj == null) {
                    obj = new SingletonLazy();
                }
            }
        }

        return obj;
    }

测试懒汉式:

public class Test2 {
    public static void main(String[] args) {
        SingletonLazy instance = SingletonLazy.getInstance();
        SingletonLazy instance2 = SingletonLazy.getInstance();
        System.out.println(instance == instance2);
    }
}

结果:
【JavaEE】单例模式

关于单例模式的实现方式并不止懒汉式饿汉式这两种,其他的暂时不做拓展,懒汉式饿汉式是很经典的两种实现方式,先把这两种掌握,之后遇到其他的实现方式再做拓展。