【单例模式的写法(线程安全)】

大家好,今天我们来讲一下单例模式,这种模式在我们的校招中最常见,而且也是我们最常考的知识点之一,下面为大家讲解一下,希望大家看完会有所帮助.互关三联,点赞收藏.💞💞💞

1.什么是单例模型

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

单例 =>单个实例(instance) 对象

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。一个程序中只能创建一个实例(对象),不能创建多个对象,java的单例模式借助语法,保证某个类只能创建一个对象,不能new多个对象,让所有需要调用的地方都共享这一单例对象。
同时单例模式也是我们校招中最常考的模式之一
在这里插入图片描述

2.单例模式的写法

单例模式的写法有很多,大约5,6种,但是我们这里只讲述两种方法饿汉模式和懒汉模式
给大家举个例子:
比如我们要打开硬盘上的文件时.读取文件内容是
饿汉模式:直接就把文件上内容全读取到内存上
懒汉模式:读取文件一小部分,到当前屏幕上,如果用户翻页了就读取其他内容,不翻页就省下来了
当我们的文件很大时 10G,饿汉模式不知道内存够不够,可能卡了半天打不开
但是我们的懒汉模式很快就能打开了

下面我们来细致讲讲两种模式

2.1饿汉模式

private static Singletn instace =new Singletn();
此时instance就是唯一的实例,因为在类中,只有一个对象,而类成员变量也是唯一的

//把这个例设成单例
//饿汉模式
//只是读操作
class Singletn {
     //static 修饰,是类属性,类直接调用
    //JVM中每个类对象只有一份,类对象里面的成员自然也是一份
     private static  Singletn instace =new Singletn();

     //获取实例方法
     public  static  Singletn getInstance()
     {
         return  instace;
     }

     //设置成私有类,外部类不能使用
     private Singletn(){

     }
}

在我们的main中就不能new一个新的对象了,因为我们在上面的类中private修饰的对象

public class ThreadDemo10 {
    public static void main(String[] args) {
        //s1和s2是同一个对象
        Singletn s1= Singletn.getInstance();
        Singletn s2= Singletn.getInstance();

        //Singletn s3=new Singletn();  //错误的不能new
    }
}

同时再类内部把实例创建好,在外部不能创建实例就可以吃保证单例的特性了

2.2懒汉模式

什么是懒汉模式?
就是类加载的时候不创建实例. 第一次使用的时候才创建实例

比如下面这个代码,先创建一个类对象,但是为空,在第一次使用的时候才会创建

//懒汉单例模式
//有读操作有写操作
class Singletnlazy {
    volatile  private static Singletnlazy instance=null;

    //volatile  保存内存可见性
    //保存       禁止指令重排序
    public static Singletnlazy getInstance()
    {
         if (instance == null)
        {
             instance = new Singletnlazy();
         }
        return instance;

    }

    private Singletnlazy()
    {

    }
}

2.3饿汉模式和懒汉模式的比较

在这里插入图片描述

大家看这两个代码,左边的是饿汉模式的,右边的是懒汉模式的
可以看出,饿汉模式只有读操作,而懒汉模式不仅有读操作还有修改操作
这就导致了饿汉模式是安全的,但是懒汉模式的多线程是不安全的

2.4懒汉模式的改进

在这里插入图片描述
在这里插入图片描述
这种情况下就会导致多new了一个对象(这个问题就很严重了)
原因在于if操作不是原子操作
这个时候我们就要变为原子操作,我们使用synchronized来让if语句和new语句一起变为原子操作

 public static Singletnlazy getInstance()
    {
     
            //保证if和new是一个原子操作
            synchronized (Singletnlazy.class)
            {
                if (instance == null)
                {
                    instance = new Singletnlazy();
                 }
            }

            return instance;

    }

但是频繁的加锁是很麻烦的,是低效的操作(可能会导致阻塞等待)
那我们就应该让降低加锁频率

我们使用双重if语句来降低锁竞争频率,只在还没创建的时候才会给加锁

public static Singletnlazy getInstance()
    {
        //使用双重 if 判定, 降低锁竞争的频率.
        if(instance==null)
        {
            //保证if和new是一个原子操作
            synchronized (Singletnlazy.class)
            {
                if (instance == null)
                {
                    instance = new Singletnlazy();

                }
            }
        }

            return instance;

    }

虽然两个if语句一样,但是作用确实差别很大的,执行时间也是天壤之别

这个时候我们的代码又出现了新的问题,指令重排序
例如原本我们的顺序是1,2,3
在这里插入图片描述
但是发生了指令重排序
就会变为1,3,2
在这里插入图片描述
虽然指令重排序的概率很小但是我们为了以防万一,我们还是要进行改进的,在创建对象的时候加一个volatile

class Singletnlazy {
    volatile  private static Singletnlazy instance=null;

    //volatile  保存内存可见性
    //保存       禁止指令重排序
    public static Singletnlazy getInstance()
    {
        //使用双重 if 判定, 降低锁竞争的频率.
        if(instance==null)
        {
            //保证if和new是一个原子操作
            synchronized (Singletnlazy.class)
            {
                if (instance == null)
                {
                    instance = new Singletnlazy();

                }
            }
        }

            return instance;

    }
}

3.小结

单例模式,线程安全问题
饿汉模式:线程是安全的,只进行读操作
懒汉模式:不安全,有读操作也有写操作

为此我们进行以下操作改进懒汉模式
在这里插入图片描述

本篇文章到这里就结束了,希望大家看完会有所收获,也希望大家多多支持,互关.💟💟💟💟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要手写一个线程安全单例模式,可以使用懒汉式和双重检查锁定的方式来实现。懒汉式是指在需要使用单例对象时才去创建它,双重检查锁定则是为了保证在多线程环境下只有一个线程能够创建该对象。 以下是一个示例代码: ```java public class Singleton { private static volatile Singleton instance; private Singleton() { // 私有构造方法 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` 在这个示例中,我们使用了volatile关键字来确保instance变量在多线程环境下的可见性。在getInstance()方法中,首先检查instance是否为null,如果为null,则进入同步块。在同步块内部再次检查instance是否为null,这是为了防止多个线程同时通过了第一个检查,进入同步块后再创建多个实例的情况。如果instance仍然为null,才会创建一个新的Singleton实例并赋值给instance变量。 需要注意的是,双重检查锁定方式只在Java 5及以上版本才能保证正确性,因为在Java 5之前的版本中,volatile关键字的语义不够强大,可能会导致创建出多个实例。而在Java 5及以上版本中,通过volatile关键字能够确保instance变量的可见性,并且禁止指令重排序,从而保证线程安全。 以上是一种手写线程安全单例模式的实现方式,通过使用双重检查锁定和volatile关键字,能够在多线程环境下保证只有一个实例被创建。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Java--设计模式之单例模式+保证线程安全的5种写法(懒、汉、内、双、枚)](https://blog.csdn.net/JustinQin/article/details/120668592)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值