懒汉模式在多线程中的问题

由上一篇,了解到懒汉以及饿汉模式,目的是
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。


但是,在多线程的情况下,会出现一些问题,如下代码,创建懒汉模式,但是通过同时 new 了两个线程来创建,此时两个线程一起走,但是创建需要延迟 2s 秒钟,所以创建的时候,判断 instance 都是 null,也就是说,两个线程创建了两个不同的对象,如图:

懒汉模式在多线程中的问题

package demo11;

/**
 * @Classname LazyMode
 * @Description 懒汉模式优化
 * @Date 2021/9/24 14:37
 * @Created by thx
 */
public class LazyMode {
    public static void main(String[] args) {
        new Thread(() -> {
            TvStation tv = TvStation.getInstance();
            System.out.println(Thread.currentThread().getId() + " " + tv);
        }).start();
        new Thread(() -> {
            TvStation tv = TvStation.getInstance();
            System.out.println(Thread.currentThread().getId() + " " + tv);
        }).start();
    }
}

class TvStation {
    /**
     * 懒汉模式
     */
    private static TvStation instance = null;
    private TvStation() {
        System.out.println("开始创建超市,需要 2s ");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("创建结束");
    }

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

那么如何解决这一问题呢?

使用 synchronized,然后再判断一次 空,此方法由于判断两次,所以叫 double-check

package demo11;

/**
 * @Classname LazyMode
 * @Description 懒汉模式优化
 * @Date 2021/9/24 14:37
 * @Created by thx
 */
public class LazyMode {
    public static void main(String[] args) {
        new Thread(() -> {
            TvStation tv = TvStation.getInstance();
            System.out.println(Thread.currentThread().getId() + " " + tv);
        }).start();
        new Thread(() -> {
            TvStation tv = TvStation.getInstance();
            System.out.println(Thread.currentThread().getId() + " " + tv);
        }).start();
    }
}

class TvStation {
    /**
     * 懒汉模式
     */
    private static TvStation instance = null;

    private TvStation() {
        System.out.println("开始创建超市,需要 2s ");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("创建结束");
    }

    public static TvStation getInstance() {
//        double-check 模式
        if (instance == null) {
            synchronized (TvStation.class) {
                if (instance == null) {
                    instance = new TvStation();
                }
            }
        }
        return instance;
    }
}

此问题只在懒汉模式有效,饿汉模式没有这个问题。

枚举天生适应单例属性:

package demo11;

import javax.swing.plaf.TableHeaderUI;

/**
 * @Classname EnumMode
 * @Description
 * @Date 2021/9/24 14:56
 * @Created by thx
 */
public class EnumMode {
    public static void main(String[] args) {
        RadioStation rs1 = RadioStation.CENTER;
        RadioStation rs2 = RadioStation.CENTER;
        System.out.println(rs1 + " " + rs2);

        new Thread(() -> {
            RadioStation rs3 = RadioStation.CENTER;
        }).start();
        new Thread(() -> {
            RadioStation rs4 = RadioStation.CENTER;
        }).start();
    }
}
enum RadioStation {
    CENTER;
    RadioStation() {
        System.out.println("开始创建电视台,需要 2s ");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("创建结束");
    }
}

上一篇:14-axios中的拦截器


下一篇:单例模式