设计模式(单例模式)

概念

保证指定的类只有一个实例,不能创建出其他的实例

实现方式

1.饿汉模式

1.1 代码展示

java 复制代码
package 设计模式;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: wuyulin
 * Date: 2023-07-28
 * Time: 11:28
 */

//单例模式(饿汉模式)
//保证Singleton类只有一个实例
//Singleton类中instance对象的创建时机:在Singleton类被jvm加载的时候创建,Singleton类会在第一次使用的时候被加载,不一定是程序一启动的时候
class Singleton{
    //带有static的是类属性,由于每个类的类对象是单例的,所以类对象的属性(static)也是单例的
    private static Singleton instance=new Singleton();
    public static Singleton getSingleton(){
        return instance;
    }   //当多个线程调用getSingleton方法相当于多个线程读取同一个变量,这是线程安全的

    //由于要规定在类的外部不能实例化Singleton类型的对象,所以要把构造函数改为私有的,这样就在类外部就不能实例化Singleton类的对象了
    private Singleton(){};
}
public class Deom1 {
    public static void main(String[] args) {
        Singleton s1=Singleton.getSingleton();
        Singleton s2=Singleton.getSingleton();

        //Singleton s3=new Singleton();

        System.out.println(s1==s2);
        //System.out.println(s1==s3);
    }
}

1.2 Singleton类中instance对象的创建时机

Singleton类中instance对象的创建时机:在Singleton类被jvm加载的时候创建,Singleton类会在第一次使用的时候被加载,不一定是程序一启动的时候

1.3 控制Singleton类的实例只有一个的核心步骤

1.3.1 在类属性(static)中进行创建,带有static的是类属性,由于每个类的类对象是单例的,所以类对象的属性(static)也是单例的

1.3.2 提供一个获取这个对象的接口,方便在类外调用这个对象

1.3.3 由于规定在类的外部不能实例化Singleton类型的对象,所以要把构造函数改为私有的,这样就在类外部就不能实例化Singleton类的对象了

1.4线程安全问题

饿汉模式实现单例模式是没有线程安全问题的,因为多个线程调用getSingleton()方法,相当于多个线程读取instance引用的值,没有涉及到多个线程进行修改,所以没有线程安全问题

2.懒汉模式

2.1代码展示

java 复制代码
package 设计模式;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: wuyulin
 * Date: 2023-07-28
 * Time: 12:05
 */
//单例线程(懒汉模式)
//SingletonLazy类中instance对象创建的时机:在第一次调用getInstance()方法时创建
class SingletonLazy{
    private static volatile SingletonLazy instance=null;    //加上volatile避免内存可见性问题
    public static SingletonLazy getInstance(){
        if(instance==null){ //第一个条件判断是否需要加锁
            synchronized(SingletonLazy.class){  //第二个条件用于判断是否要创建对象
                if(instance==null){
                    instance=new SingletonLazy();
                }
            }
        }
        return instance;
    }

    private SingletonLazy(){};
}
public class Demo2 {
    public static void main(String[] args) {
        SingletonLazy s1=SingletonLazy.getInstance();
        SingletonLazy s2=SingletonLazy.getInstance();

        System.out.println(s1==s2);

        //SingletonLazy s3=new SingletonLazy();
    }



}

2.2SingletonLazy类中instance对象创建的时机

在第一次调用getInstance()方法时创建

2.3控制SingletonLazy类的实例只有一个的核心步骤

2.3.1在类属性(static)中进行创建声明,带有static的是类属性,由于每个类的类对象是单例的,所以类对象的属性(static)也是单例的

2.3.2 提供一个实例化这个对象并返回这个对象的接口

2.3.3 由于规定在类的外部不能实例化Singleton类型的对象,所以要把构造函数改为私有的,这样就在类外部就不能实例化Singleton类的对象了

2.4 线程安全问题

懒汉模式存在线程安全问题

当多个线程判断instance=null,可能多个线程都会判断为null值,导致创建多个对象,所以要对getInstance()方法中的代码进行加锁,将判断和创建对象两个操作变成一个原子性的操作

但实际上是需要一直加锁的吗?不是的,只有我们准备实例化instance对象的时候会出现线程安全问题,当创建成功后,instance!=null,此时多个线程都是获得instance引用的值,不存在线程安全问题,也就不需要加锁了,所以要判断一下是否需要加锁,当instance为null表示当前第一次准备创建该对象,此时加锁控制只有第一个线程创建对象,其余线程返回instance的值,instance不为null说明instance对象已经创建成功,多个线程都只是读取instance的值,不存在线程安全问题,所以不用加锁

加了锁以后该程序其实还有内存可见性问题,因为在第一次调用getInstance()方法创建对象后,第二次去读取instance的值,你不一定能读到最新的instance的值,原因请看线程安全问题(内存可见性),所以要在instance变量声明的时候加上volatile(易变的),解决内存可见性问题

使用volatile不仅仅是为了预防出现内存可见性问题,还要解决指令重排序问题,指令重排序问题请看线程安全问题(指令重排序)

相关推荐
AronTing几秒前
命令模式:从撤销操作到分布式调度的命令封装实践
java·后端·架构
liwulin05064 分钟前
【JAVA】在idea新加artifact时,点击Build-Build Artifacts时,新加的artifact不能选中
java·pycharm·intellij-idea
okok__TXF4 分钟前
Spring分析-IOC
java·后端·spring
老码识土8 分钟前
Kotlin 协程源代码泛读:suspend 函数
java
顾林海9 分钟前
深度解析ConcurrentHashMap工作原理
android·java·面试
末央&9 分钟前
【C++】特化妙技与分文件编写 “雷区”
开发语言·c++·算法
一个天蝎座 白勺 程序猿20 分钟前
Python(19)Python并发编程:深入解析多线程与多进程的差异及锁机制实战
开发语言·python
ShiinaMashirol22 分钟前
代码随想录刷题|Day20(组合总数,组合总数2、分割回文串)
java·算法
都叫我大帅哥25 分钟前
代码界的「俄罗斯套娃」:组合模式的嵌套艺术
java·后端·设计模式