15-单例模式

目录

1.设计模式是啥?

2.单例模式是啥?

3.单例模式的实现步骤

3.1.设置私有的构造函数(为了防止其他类直接new此对象)。

3.2.声明⼀个私有的对象属性。

3.3.提供⼀个公共的获取实例的⽅法。

4.单例模式具体的实现方式:分成 "饿汉" 和 "懒汉" 两种

4.1.饿汉方式

4.2.懒汉方式

a.全局加锁(线程安全、性能比较低)

b.局部加锁【最终版本】(线程安全,双重效验锁DCL(Double Check Lock))


1.设计模式是啥?

设计模式好⽐象棋中的 "棋谱",红⽅当头炮, ⿊⽅⻢来跳。针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀些固定的套路。按照套路来⾛局势就不会吃亏。

软件开发中也有很多常⻅的 "问题场景",针对这些问题场景, ⼤佬们总结出了⼀些固定的套路,按照这个套路来实现代码, 也不会吃亏。

2.单例模式是啥?

单例模式是校招中最常考的设计模式之一。

单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例。

这⼀点在很多场景上都需要。⽐如 JDBC 中的 DataSource 实例就只需要⼀个。

3.单例模式的实现步骤

3.1.设置私有的构造函数(为了防止其他类直接new此对象)。

3.2.声明⼀个私有的对象属性。

3.3.提供⼀个公共的获取实例的⽅法。

4.单例模式具体的实现方式:分成 "饿汉" 和 "懒汉" 两种

4.1.饿汉方式

程序启动之后,立马创建单例对象。

优点:线程安全的。

缺点:可能造成资源的浪费,通常不使用。

/**
 * DataSource单例模式
 * 饿汉模式
 */
public class DataSourceSingleton {
     //1.创建一个私有的构造函数(为防止其他类直接new此对象)
     private DataSourceSingleton() {}
    
     //2.创建一个私有的属性对象
     private static DataSourceSingleton dataSource = new DataSourceSingleton(); //随着程序(JVM)启动而执行
     
     //3.创建一个公共的对外暴露的单例对象
     public static DataSourceSingleton getInstance() {
         return dataSource;
     }
}
public class DataSourceTest {
    public static void main(String[] args) {
        System.out.println(DataSourceSingleton.getInstance());
    }
}

4.2.懒汉方式

当有程序调用单例对象时才初始化,可以避免资源不必要的浪费。

public class DataSourceSingleton2 {
    //1.私有的构造方法
    private DataSourceSingleton2() {
    }

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

    //3.创建一个对外提供访问的单例对象
    public static DataSourceSingleton2 getInstance() {
        if(dataSource == null){ //第一次访问
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            dataSource = new DataSourceSingleton2(); //当线程1和线程2同时走到这里,线程1->null->对象1;线程2->null->对象2.不满足单例模式的定义。
        }
        return dataSource;
    }
}
public class DataSourceTest {
    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();
    }
}

默认的懒汉模式是非线程安全的,使用要对懒汉进行优化,优化改进:

a.全局加锁(线程安全、性能比较低)

/**
 * 单例模式--懒汉模式1
 */
public class DataSourceSingleton2 {
    //1.私有的构造方法
    private DataSourceSingleton2() {
    }

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

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

b.局部加锁【最终版本】(线程安全,双重效验锁DCL(Double Check Lock))

/**
 * 单例模式--懒汉模式2
 */
public class DataSourceSingleton3 {
    //1.私有的构造方法
    private DataSourceSingleton3() {}

    //2.私有属性
    private static volatile DataSourceSingleton3 dataSource = null; //属性标识为volatile

    //3.公共的方法,得到单例对象
    public static DataSourceSingleton3 getInstance() {
        if (dataSource == null) { //大致分流状态 DCL
            synchronized (DataSourceSingleton3.class) { //排队执行
                if(dataSource == null) { //精细化判断(分流) DCL
                    dataSource = new DataSourceSingleton3();
                }
            }
        }
         return dataSource;
    }
}

若属性没有标识为volatile,则对象创建需要三步:

  1. memory = allocate() //分配内存

  2. ctorInstanc(memory) //初始化对象

  3. instance = memory //设置instance指向刚分配的地址

假如因为指令重排导致执⾏的顺序变为了132,那么假如a线程中执⾏完13之后,b线程到达代码2处,执⾏判断语句,发现instance指向的是⼀段地址,因此直接不进⼊判断语句,⽽是直接返回了⼀个没有初始化的空的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值