【多线程案例】单例模式(懒汉模式和饿汉模式)

news2024/11/17 7:30:17

文章目录

    • 1. 什么是单例模式?
    • 2. 立即加载/“饿汉模式”
    • 3. 延时加载/“懒汉模式”
      • 3.1 第一版
      • 3.2 第二版
      • 3.3 第三版
      • 3.4 第四版

1. 什么是单例模式?

提起单例模式,就必须介绍设计模式,而设计模式就是在软件设计中,针对特殊问题提出的解决方案。它是多年来针对一些常见的问题的解决方法,具有良好的可复用性、可扩展性和可维护性。
标准的设计模式有23种,单例模式就是最常见的一种,其目的是确认一个类只有一个实例对象,并且可以提供一个全局访问点来访问该实例。
单例就是指一个类的实例只有一个,即该类的所有对象都指向用同一个实例。
而多数单例模式并没有结合多线程,在多线程环境下运行有时会出现线程安全问题,所以下面不仅介绍如何实现单例模式,还有单例模式结合多线程使用时的相关知识。

2. 立即加载/“饿汉模式”

立即加载一般还被称作饿汉模式,根据立即,饿汉可以看出十分的急,所以在饿汉模式中,这样单例中的唯一实例对象就被创建。

创建MyObject.java代码如下:

//单例模式、饿汉模式
public class MyObject {
    //进行封装,防止创建新的对象
    private static MyObject object = new MyObject();
    private MyObject(){};
    //通过这个方法获得对象
    public static MyObject getObject(){
        return object;
    }
}

创建线程类MyThread.java:

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(MyObject.getObject().hashCode());
    }
}

创建运行类Run.java:

public class Run {
    //测试单例模式对象是同一个
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:
运行1
运行结果相同,说明对象是同一个,成功实现了立即加载型单例设计模式。

3. 延时加载/“懒汉模式”

延时,懒汉可以看出代码并不着急,所以懒汉模式型单例模式中的对象并不像饿汉模式中没有调用前就创建完成,而是在第一调用方法实例时才被创建。
对比饿汉模式:
优点:会减少内存资源浪费。
缺点:多线程环境并发运行,可能会出现线程安全。

3.1 第一版

创建类MyObjectLazy.java,代码如下:

public class MyObjectLazy {
    //单例模式、懒汉模式
    private static MyObjectLazy myObjectLazy = null;
    private static Object lock = new Object();
    private MyObjectLazy(){};
    public static MyObjectLazy getMyObjectLazy(){
        if(myObjectLazy != null){
            return myObjectLazy;
        }else{
            myObjectLazy = new MyObjectLazy();
        }
        return myObjectLazy;
    }
 }

创建线程类MyThreadLazy.java:

public class MyThreadLazy extends Thread{
    @Override
    public void run() {
        System.out.println(MyObjectLazy.getMyObjectLazy().hashCode());
    }
}

创建运行类RunLazy.java:

public class RunLazy {
    //测试对象是不是同一个,是的话就是安全的单例模式
    public static void main(String[] args) {
        MyThreadLazy t1 = new MyThreadLazy();
        MyThreadLazy t2 = new MyThreadLazy();
        MyThreadLazy t3 = new MyThreadLazy();
        t1.start();
        t2.start();
        t3.start();
    }
}

运行结果:
1
结果不同,所以并不是单例模式,其中有问题,造成线程不安全。

3.2 第二版

第一版生成不同对象,所以造成非线程安全,我们可以做出一点修改,对代码加上锁。
修改后的MyObjectLazy.java:

    public static MyObjectLazy getMyObjectLazy(){
        synchronized (lock){
            if(myObjectLazy == null){
                myObjectLazy = new MyObjectLazy();
            }
        }
        return myObjectLazy;
    }

运行结果:
运行3
说明这个单例模式是正确实现了。

3.3 第三版

但是第二版又暴露一个问题,上面加锁后相当于整个方法都加锁,上面一个线程没有释放锁,下一个线程将无法运行,造成效率低下。
所以我们继续修改,修改后的MyObjectLazy.java:

    public static MyObjectLazy getMyObjectLazy(){
        try{
            if(myObjectLazy == null){
                Thread.sleep(1000);
                synchronized (lock){
                    myObjectLazy = new MyObjectLazy();
                }
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return myObjectLazy;
    }

运行结果:
3
运行结果又不同了,创建出了三个对象,为什么?这是因为虽然上了锁,但是if已经判断,只是new对象时串行。
虽然效率提高了,但这并不是单例模式。

3.4 第四版

我们可以使用DCL双检查锁机制来实现,进行两次if判断,使线程安全。
修改后MyObjectLazy.java:

    //再一次修改代码,加锁只加一块,并且应用DCL双检查机制来实现多线程环境下的延时加载单例模式,保证线程安全
    public static MyObjectLazy getMyObjectLazy(){
        try{
            if(myObjectLazy == null){
                Thread.sleep(1000);
                synchronized (lock){
                    if(myObjectLazy == null) {
                        myObjectLazy = new MyObjectLazy();
                    }
                }
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        return myObjectLazy;
    }

运行结果:
4
使用双检查锁功能,成功解决了懒汉模式遇到多线程的问题。DCL经常出现在此场景下,我们要学会应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/963134.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无涯教程-JavaScript - HYPGEOMDIST函数

HYPGEOMDIST函数替代Excel 2010中的HYPGEOM.DIST函数。 描述 该函数返回超几何分布。 HYPGEOMDIST返回给定样本数量,给定样本数量,总体成功率和总体数量的概率。 将HYPGEOMDIST用于具有有限总体的问题,其中每个观察输出都是成功或失败,并且给定大小的每个子集的选择可能性均…

《数字图像处理-OpenCV/Python》连载(4)图像的读取与保存

《数字图像处理-OpenCV/Python》连载(4)图像的读取与保存 本书京东优惠购书链接:https://item.jd.com/14098452.html 本书CSDN独家连载专栏:https://blog.csdn.net/youcans/category_12418787.html 第1章 图像的基本操作 为了方…

小兔鲜商02

npm i vueuse/core -fvue插件使用: 许多公用的全局组件,,可以通过插件注册进去,就不用一个一个导入组件,, import XtxSkeleton from /components/library/xtx-skeletonexport default {install (app) {// …

ELK高级搜索(三)

文章目录 11.索引Index入门11.1 索引管理11.2 定制分词器11.3 type底层结构11.4 定制dynamic mapping11.5 零停机重建索引 12.中文分词器 IK分词器12.1 Ik分词器安装使用12.2 ik配置文件12.3 使用mysql热更新 13.java api 实现索引管理14&…

Spring源码解析-总览

1、前言 Spring源码一直贯穿我们Java的开发中,只要你是一个Java开发人员就一定知道Spring全家桶。Spring全家桶为我们一共一站式服务,IOC、AOP更是Spring显著特性。但是Spring到底怎么为我们提供容器,管理资源的呢?下来&#xff0…

1.4状态机模型

状态机简介: 1.大盗阿福 阿福是一名经验丰富的大盗。趁着月黑风高,阿福打算今晚洗劫一条街上的店铺。 这条街上一共有 N N N家店铺,每家店中都有一些现金。 阿福事先调查得知,只有当他同时洗劫了两家相邻的店铺时&#xff0…

js对中文进行base64编码和解码操作,解决中文乱码问题

我使用github api的接口获取文件内容,然后使用atob进行解码,但是发现:乱码.......糟心啊 所以就有了我封装的方法: export const encode64 (str) > {// 首先,我们使用 encodeURIComponent 来获得百分比编码的UTF…

m1芯片macOS系统卡顿问题解决方法

m1芯片的MacBook在使用过程中会出现“假死”的情况。主要表现为鼠标转圈圈,很多操作都不能实现,不能输入文本,系统ui也响应十分慢,而资源监视却看不到很高的占用。一般出现此类情况只能关机或重启。这其中的"罪魁祸首"便…

管理类联考——逻辑——形式逻辑——汇总篇——知识点突破——形式逻辑——联言选言假言——矛盾

角度 角度——汇总——持续优化 性质 (1) 所有的 S 是 P 所有的S是P 所有的S是P 与 有的 S 不是 P 有的S不是P 有的S不是P矛盾 (2) 所有的 S 不是 P 所有的S不是P 所有的S不是P 与 有的 S 是 P 有的S是P 有的S是P 矛盾 &#xf…

Spring源码解析(十):spring整合mybatis源码

Spring源码系列文章 Spring源码解析(一):环境搭建 Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三):bean容器的刷新 Spring源码解析(四):单例bean的创建流程 Spring源码解析(五)&…

计算机毕设之基于数据可视化的智慧社区内网平台python+django+mysql(含开题+源码+部署教程)

系统阐述的是一款基于数据可视化的智慧社区内网平台的设计与实现,对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计,描述,实现和分析与测试方面来表明开发的过程。开发中使用了 django框架和MySql数据库技术搭建系…

el表达式和标签库的简单使用!!!

备工作1:tomcat10.1.13版本,可去官网下载,不会下载的童靴看过来:如何正确下载tomcat???_明天更新的博客-CSDN博客 准备工作2:添加架包(需要三个架包) jstl架包…

老师们快看过来,这里有使用ChatGPT当助教的方法

最近OpenAI官方博客发布了一篇文章How teachers are using ChatGPT(老师们如何使用ChatGPT),讲的是老师们如何在教学中使用ChatGPT,其中有几个例子挺好的,我转述一下,希望对你有用。 制定教案 第一个例子…

[git]分支操作

Checkout 相当于切换到该分支,但是因为不能直接操作远程分支,会在本地同步一个完全一样的分支。 注意:切换分支前本地先进行提交(addcommit),否则有可能代码会丢失。 New Branch from Selected... 创建一…

【小沐学Unity3d】3ds Max 多维子材质编辑(Multi/Sub-object)

文章目录 1、简介2、精简材质编辑器2.1 先创建多维子材质,后指定它2.2 先指定标准材质,后自动创建多维子材质 3、Slate材质编辑器3.1 编辑器简介3.2 编辑器使用 结语 1、简介 多维子材质(Multi/Sub-object)是为一个模形&#xff0…

信息安全检测和应用信息系统安全测试

安全测试 信息安全检测,为软件/信息系统出具的软件检测报告(或第三方检测报告、软件安全测试报告),是信息系统/软件上线前都需要的测试报告。 信息安全检测的标准: 信息安全检测依据DB31/T272-2008《计算机信息系统…

【ICer的脚本练习】“精通各种语言的hello world!“

系列的目录说明请见:ICer的脚本练习专栏介绍与全流程目录_尼德兰的喵的博客-CSDN博客 前言 这一节呢主要是检查一下Linux和win环境是不是能正常的支持咱们的脚本学习,所以来答应各种语言的hello world!,毕竟打印了就是学会了٩(๑❛ᴗ❛๑)۶…

Nex.js Web 应用程序 SSG 与 SSR——选择正确的渲染方法

Next.js,一个流行的React框架,改变了开发人员构建现代Web应用程序的方式。它提供了强大的功能,例如服务器端渲染 (SSR) 和静态站点生成 (SSG),可优化应用程序的性能和用户体验。在这…

03. 程序在内存中被CPU执行

1. 程序是什么? 程序是由指令和数据组成的。 当我们使用计算机运行一个程序时,计算机会读取程序中的指令一步步执行,直到达到程序结束的地方。 程序的指令:就像一份菜谱,告诉计算机按照哪些步骤来做事情。 程序的数…

【原创】H3C三层交换机的路由模式

网络拓扑图 将三层交换机当路由器使用 交换机配置 <H3C>dis stp briefMST ID Port Role STP State Protection0 GigabitEthernet1/0/1 DESI LEARNING NONE0 GigabitEthernet1/0/2 …