设计模式(单例模式)

概念

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

实现方式

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

相关推荐
Cx330❀7 分钟前
《C++ 多态》三大面向对象编程——多态:虚函数机制、重写规范与现代C++多态控制全概要
开发语言·数据结构·c++·算法·面试
seabirdssss11 分钟前
JDK 11 环境正确,端口未被占用,但是运行 Tomcat 11 仍然闪退
java·开发语言·tomcat
努力学算法的蒟蒻12 分钟前
day03(11.1)——leetcode面试经典150
java·算法·leetcode
Mr YiRan23 分钟前
SYN关键字辨析,各种锁优缺点分析和面试题讲解
java·开发语言
Zhang青山1 小时前
028.爬虫专用浏览器-抓取#shadowRoot(closed)下
java·后端
bug攻城狮1 小时前
SpringBoot响应封装:Graceful Response vs 自定义通用响应类选型指南
java·spring boot·后端·http
oioihoii1 小时前
智驾“请抬脚”提示感悟 - 当工程师思维遇见用户思维
开发语言·程序员创富
m0_736927041 小时前
Spring Boot项目中如何实现接口幂等
java·开发语言·spring boot·后端·spring·面试·职场和发展
渡我白衣1 小时前
C++:链接的两难 —— ODR中的强与弱符号机制
开发语言·c++·人工智能·深度学习·网络协议·算法·机器学习
小龙报1 小时前
《算法通关指南:数据结构和算法篇 --- 顺序表相关算法题》--- 1.移动零,2.颜色分类
c语言·开发语言·数据结构·c++·算法·学习方法·visual studio