单例模式详解

一、定义

单例模式是指在内存中只会创建一次且仅创建一次对象的设计模式。单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例。

二、单例模式的实现

实现分为两种:
a. 饿汉方式:程序启动之后,里面创建单例对象
(不存在线程安全问题,但可能会造成资源的浪费)
b. 懒汉方式:当程序调用单例对象的时候才初始化(使⽤时才加载,可以避免资源不必要的浪费)

单例模式的实现步骤:
(饿汉模式和懒汉模式实现模式一模一样)
1.设置私有的构造函数;
2. 声明⼀个私有的对象属性;
3. 提供⼀个公共的获取实例的⽅法。

2.1饿汉模式

不存在线程安全问题

/**
 * DataSource的单例模式
 * 饿汉模式
 */
public class DataSourceSingleton {
    //1.提供一个私有的构造方法(防止外部直接new对象)
    private DataSourceSingleton(){
    }
    //2.创建一个私有的属性对象
    private static DataSourceSingleton dataSource = new DataSourceSingleton();
    //3.提供公共的对外的单例对象
    public static DataSourceSingleton getInstance(){
        return dataSource;
    }

    public static void main(String[] args) {
        System.out.println(DataSourceSingleton.getInstance());
    }
}

2.2懒汉模式

2.2.1普通懒汉模式

存在线程安全问题

/**
 * 单例模式的懒汉模式 1
 */
public class DataSourceSingleton2 {
    //1.创建一个私有的构造方法(防止外部直接new对象)
    private DataSourceSingleton2(){
    }

    //2.创建一个私有的属性
    private static DataSourceSingleton2 dataSource;

    //3.创建一个对外提供访问的单例对象
    public static DataSourceSingleton2 getInstance(){
        if (dataSource == null){
            //第一次访问
            dataSource = new DataSourceSingleton2();
        }
        return dataSource;
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(DataSourceSingleton2.getInstance()); 
        });

        Thread t2 = new Thread(() -> {
            System.out.println(DataSourceSingleton2.getInstance());
        });
        t1.start();
        t2.start();
    }
}

在这里插入图片描述
结果发现这两个地址完全不同,所以不再满足单例模式
是非线程安全的代码

问题解决:
加锁synchronized可以解决非线程安全问题

/**
 * 单例模式的懒汉模式 1
 */
public class DataSourceSingleton2 {
    //1.创建一个私有的构造方法(防止外部直接new对象)
    private DataSourceSingleton2(){
    }

    //2.创建一个私有的属性
    private static DataSourceSingleton2 dataSource;

    //3.创建一个对外提供访问的单例对象
    public synchronized static DataSourceSingleton2 getInstance(){
        if (dataSource == null){
            //第一次访问
            dataSource = new DataSourceSingleton2();
        }
        return dataSource;
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println(DataSourceSingleton2.getInstance());
        });

        Thread t2 = new Thread(() -> {
            System.out.println(DataSourceSingleton2.getInstance());
        });
        t1.start();
        t2.start();
    }
}

在这里插入图片描述
虽然解决了线程安全问题,但耗费了资源,不是最高性能。

2.2.2DCL版懒汉模式

双重校验加锁

/**
 * 懒汉模式 -3/4
 */
public class DataSourceSingleton3 {
    //1.创建一个私有的构造方法
    private DataSourceSingleton3(){
    }
    //2.创建一个私有属性
    private static DataSourceSingleton3 dataSource;
    //3.创建一个对外提供访问的单例对象
    public static DataSourceSingleton3 getInstance(){
            if (dataSource == null){//大致分流
                synchronized (DataSourceSingleton3.class){
                    if (dataSource == null){//精细化的判断
                        dataSource = new DataSourceSingleton3();
                    }
                }
            }
        return dataSource;

    }
}

上面模式还存在非线程安全问题,指令重排序

使用volatile模式(单例模式最终版本)

new一个对象,在JVM中会经过三步:
1.给dataSource分配内存空间
2. 初始化dataSource对象
3.将dataSource指向分配好的内存空间
指令重排序之后,顺序可能会变为:1-3-2
会导致多个线程创建对象时,有可能线程A创建对象的过程中,执行了第1、3步,线程B 判断对象已经不为空,获取到未初始化的对象,直接返回了一个没有初始化的空的对象。

/**
 * 懒汉模式 -3/4
 */
public class DataSourceSingleton3 {
    //1.创建一个私有的构造方法
    private DataSourceSingleton3(){
    }
    //2.创建一个私有属性
    private static volatile DataSourceSingleton3 dataSource;
    //3.创建一个对外提供访问的单例对象
    public static DataSourceSingleton3 getInstance(){
            if (dataSource == null){//大致分流
                synchronized (DataSourceSingleton3.class){
                    if (dataSource == null){//精细化的判断
                        dataSource = new DataSourceSingleton3();
                    }
                }
            }
        return dataSource;

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值