【JavaEE】单例模式

发布于:2022-12-15 ⋅ 阅读:(405) ⋅ 点赞:(0)

饿汉模式

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

    public static Singleton getInstance() {
        return instance;
    }

    /**
     * private : 如果不将构造方法的访问修饰限定符修改为 private 那么就会被构造出多个实例 就违背了单例模式的初衷
     */
    private Singleton() {}
}

懒汉模式

class Singleton2 {

     private volatile static Singleton2 instance;

    public static Singleton2 getInstance() {
        if (instance == null) {
            synchronized (Singleton2.class) {
                if (instance == null) {
                    instance = new Singleton2();
                }
            }
        }
        return instance;
    }

    private Singleton2() {}

}

总结

在多线程下,饿汉模式是线程安全的,懒汉模式是线程不安全的;

在这里我们再复习一下引起线程不安全的原因:

1.多线程下,抢占式执行(操作系统的随机调度)

2.多个线程修改同一个变量

3.修改操作不是原子的

4.内存可见性

5.指令重排序

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

懒汉模式:

当一个线程t1正在创建对象并赋值给instance,但并没有执行完时,另一个线程t2读到了还没有修改完的值,因此又重新创建了一个新的对象,此时就会导致实例被创建出多份儿了。

那么如何解决呢? - 加锁

public static Singleton2 getInstance() {
    synchronized (Singleton2.class) {
        if (instance == null) {
            instance = new Singleton2();
        }
    }
    return instance;
}

此时就不会出现上述问题了,但是又有新的问题出现了

仔细观察我们可以发现,我们只需要在第一次进行加锁操作,当时里被创建后便不需要了,直接返回即可,如果每次都需要加锁,那岂不是很烦。因此我们需要在进入加锁操作前再加上一层if语句,来判断一下是否需要创建实例。

public static Singleton2 getInstance() {
    if (instance == null) {
        synchronized (Singleton2.class) {
            if (instance == null) {
                instance = new Singleton2();
            }
        }
    }
    return instance;
}

上述代码,还有一个非常重要的问题!

假设两个线程t1,t2调用getInstance方法,t1拿到锁,进入new操作,这里的new操作可以粗略的分成三个步骤:

1.申请内存,得到内存首地址

2.调用构造方法,初始化实例

3.将内存首地址赋值给instance

上述过程编译器可能会进行“指令重排序”的优化

如果是单线程,指令2和3发生调换是无所谓的

如果是多线程,就会出现问题。

假设t1线程拿到锁正在进行 new 操作 ,且发生指令重排序,先执行指令3,此时线程t2判断instance不为空,直接返回instance,那么我们知道,此时t1线程虽然把内存首地址赋值给了instance,但里面的数据是无效的,而t2线程拿到instance后,可能会对其进行解引用操作(使用里面的属性/方法),就会出现问题!

那么如何防止呢?也是需要用到 volatile 关键字 - 防止指令重排序

private volatile static Singleton2 instance;


网站公告

今日签到

点亮在社区的每一天
去签到