设计模式(单例模式)

概念

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

实现方式

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

相关推荐
理想不理想v4 分钟前
使用JS实现文件流转换excel?
java·前端·javascript·css·vue.js·spring·面试
hutaotaotao6 分钟前
c语言用户不同命令调用不同函数实现
c语言·开发语言
huangjiazhi_7 分钟前
QTcpSocket 服务端和客户端
开发语言·qt
ac-er888822 分钟前
ThinkPHP中的MVC分层是什么
开发语言·php·mvc
惜.己24 分钟前
Jmeter中的配置原件(四)
java·前端·功能测试·jmeter·1024程序员节
槿花Hibiscus29 分钟前
C++基础:Pimpl设计模式的实现
c++·设计模式
yava_free1 小时前
JVM这个工具的使用方法
java·jvm
吾与谁归in1 小时前
【C#设计模式(4)——构建者模式(Builder Pattern)】
设计模式·c#·建造者模式
shinelord明1 小时前
【再谈设计模式】建造者模式~对象构建的指挥家
开发语言·数据结构·设计模式
黑不拉几的小白兔1 小时前
PTA部分题目C++重练
开发语言·c++·算法