设计模式(单例模式)

概念

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

实现方式

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不仅仅是为了预防出现内存可见性问题,还要解决指令重排序问题,指令重排序问题请看线程安全问题(指令重排序)

相关推荐
努力努力再努力wz30 分钟前
【Linux网络系列】:JSON+HTTP,用C++手搓一个web计算器服务器!
java·linux·运维·服务器·c语言·数据结构·c++
魂梦翩跹如雨31 分钟前
死磕排序算法:手撕快速排序的四种姿势(Hoare、挖坑、前后指针 + 非递归)
java·数据结构·算法
带刺的坐椅8 小时前
Solon AI Skills 会是 Agent 的未来吗?
java·agent·langchain4j·solon-ai
jacGJ8 小时前
记录学习--文件读写
java·前端·学习
哈哈不让取名字8 小时前
基于C++的爬虫框架
开发语言·c++·算法
花间相见8 小时前
【JAVA开发】—— Nginx服务器
java·开发语言·nginx
扶苏-su8 小时前
Java---Properties 类
java·开发语言
cypking9 小时前
四、CRUD操作指南
java
2301_780669869 小时前
文件字节流输出、文件复制、关闭流的方法
java
一条咸鱼_SaltyFish10 小时前
远程鉴权中心设计:HTTP 与 gRPC 的技术决策与实践
开发语言·网络·网络协议·程序人生·http·开源软件·个人开发