设计模式(单例模式)

概念

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

实现方式

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

相关推荐
救救孩子把10 分钟前
深入理解 Java 对象的内存布局
java
落落落sss13 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节18 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭25 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
简单.is.good30 分钟前
【测试】接口测试与接口自动化
开发语言·python
我行我素,向往自由31 分钟前
速成java记录(上)
java·速成
一直学习永不止步37 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明38 分钟前
面试知识储备-多线程
java·面试·职场和发展
Yvemil71 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节