【Linux】:线程安全的单例模式

线程安全的单例模式

  • 一.STL和智能指针的安全
  • 二.单例模式
    • 1.基本概念
    • 2.懒汉和饿汉的实现方式
  • 三.常见的其它锁
  • 四.读者写者模型

一.STL和智能指针的安全

1.STL中的容器是否是线程安全的?

不是. 原因是, STL 的设计初衷是将性能挖掘到极致, 而一旦涉及到加锁保证线程安全, 会对性能造成巨大的影响.而且对于不同的容器,加锁方式的不同, 性能可能也不同(例如hash表的锁表和锁桶).因此 STL 默认不是线程安全. 如果需要在多线程环境下使用,往往需要调用者自行保证线程安全。

2.智能指针是否是线程安全的?

对于 unique_ptr, 由于只是在当前代码块范围内生效, 因此不涉及线程安全问题.

对于 shared_ptr, 多个对象需要共用一个引用计数变量, 所以会存在线程安全问题. 但是标准库实现的时候考虑到了这个问题, 基于原子操作(CAS)的方式保证 shared_ptr 能够高效, 原子的操作引用计数.

二.单例模式

1.基本概念

1.什么是单例模式

单例模式是一种 “经典的, 常用的, 常考的” 设计模式。

2.单例模式的特点

某些类, 只应该具有一个对象(实例), 就称之为单例。

3.单例模式常见的两种设计方式

饿汉方式和懒汉方式。

举个例子:

吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭.
吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式.

换言之,当我们申请空间时,饿汉是立马申请;懒汉是延时申请(当我们真的需要使用时再申请)。懒汉的优势在于可以提高我们的运行速度,因为它把申请空间的所需的时间延后了,所以我们当前运行就会变快。再例如打开一个游戏,10个G,懒汉先加载要使用的,剩下的等需要时再加载,这样游戏的运行速度变快了。

2.懒汉和饿汉的实现方式

伪代码

在这里插入图片描述

两者的主要区别在于,一个是直接创建对象,一个是先创建的对象指针,等需要时再new一个对象。static修饰的是静态变量,也是全局变量的一种,也就意味着饿汉是在程序加载到内存时就要创建对象,而懒汉不需要,所以懒汉提高了运行效率。

懒汉模式的线程安全问题

很明显,在进行多并发时,懒汉模式会存在安全问题。当一个线程判断完if后,正准备new对象,结果就被替换了,接着又来一个线程进行if判断也能通过,这样就造成了new了多个对象。解决方法也很简单,在if前面加锁就行了。进一步优化一下:当第一个对象被new出来后,其实就不再需要锁了,这样重复的加锁,解锁也会对性能造成影响,所以再在锁的前面套一个if判断,下面是优化版本。

在这里插入图片描述

三.常见的其它锁

  1. 悲观锁:在每次取数据时,总是担心数据会被其他线程修改,所以会在取数据前先加锁(读锁,写锁,行锁等),当其他线程想要访问数据时,被阻塞挂起。
  2. 乐观锁:每次取数据时候,总是乐观的认为数据不会被其他线程修改,因此不上锁。但是在更新数据前,会判断其他数据在更新前有没有对数据进行修改。主要采用两种方式:版本号机制和CAS操作。
  3. CAS操作:当需要更新数据时,判断当前内存值和之前取得的值是否相等。如果相等则用新值更新。若不等则失败,失败则重试,一般是一个自旋的过程,即不断重试。

自旋锁:

这个锁与之前的锁有些区别,之前的锁都称作挂起等待锁,顾名思义就是获取锁失败后把自己挂起。但该锁不一样,它获取锁失败后不让自己挂起,而由线程周而复始的去申请锁,在申请锁的过程中不断检测锁的状态的过程就叫做自旋。

很明显,这个过程是消耗时间的,所以使用自旋锁一般在临界资源释放时间很短的情况下使用。

在这里插入图片描述

第一个函数申请锁失败后会直接挂起,而第二个函数申请锁失败后会返回失败。所以实现一个自旋锁直接用第二个函数再外加一个while循环即可。

当然,pthread库直接提供了自旋锁的接口。

在这里插入图片描述
自旋锁阻塞版本也并不是挂起,实际上就是底层封装了while循环,当申请锁失败后一直循环,看到的效果就是阻塞了。非阻塞版本就是没有封装while。

当然该锁也有初始化,解锁,销毁…与互斥锁基本一致。

总结:对于用户而言,自旋锁#其它锁区别在于一个不会将线程挂起,一个会挂起罢了。

四.读者写者模型

在这里插入图片描述

读写锁

在编写多线程的时候,有一种情况是十分常见的。那就是,有些公共数据修改的机会比较少。相比较改写,它们读的机会反而高的多。通常而言,在读的过程中,往往伴随着查找的操作,中间耗时很长。给这种代码段加锁,会极大地降低我们程序的效率。那么有没有一种方法,可以专门处理这种多读少写的情况呢? 有,那就是读写锁。

在这里插入图片描述

加锁:读者加锁和写者加锁是不同的

写者。

在这里插入图片描述

读者。

在这里插入图片描述

在这里插入图片描述

读写者模型有一个特点:读者优先。因为读者的数量一般情况下是远远大于写者的,所以在竞争锁时,读者有更大的概率得到锁,可能会导致写者一直得不到锁,从而造成线程饥饿问题。注意:这里的线程饥饿是一个中性词,因为这本是它的特点。当然我们也可以通过修改代码来实现写者优先。

为了更好的理解,下面是伪代码

在这里插入图片描述

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

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

相关文章

router4j--SpringCloud动态路由利器

前言 本文介绍Java的动态路由中间件:router4j。router4j用于SpringCloud项目,它可以将某个url请求路由到指定的机器上,也可以将所有请求强制转到指定机器。 问题描述 Java后端在开发SpringCloud项目时如果同一个应用起了多个实例&#xff…

【K12】tk窗口+plt图像功能-学习物理中的串并联研究【附源码说明】

程序源码 import tkinter as tk import matplotlib.pyplot as plt# 初始化 matplotlib 的字体设置 plt.rcParams[font.family] SimHei# 计算串联电路的函数 def calculate_series():try:# 获取用户输入的电阻值并转换为浮点数r1 float(entry_r1.get())r2 float(entry_r2.ge…

第二篇:数据结构与算法-链表

概念 链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻, 可以给每个元素附加一个指针域,指向下一个元素的存储位 置。 每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址&…

C/C++ 跨文件共享全局变量

目录 效果 项目 代码 下载 为了实现跨文件共享全局变量,我们可以使用 extern 关键字。extern 关键字用于声明一个变量,该变量在其他地方已经定义。它告诉编译器这个变量在其他文件中已经定义了,不需要重新分配内存空间,只需要…

C语言-指针的基本知识(上)

一、关于内存 存储器:存储数据器件 外存 外存又叫外部存储器,长期存放数据,掉电不丢失数据 常见的外存设备:硬盘、flash、rom、u盘、光盘、磁带 内存 内存又叫内部存储器,暂时存放数据,掉电数据…

蓝桥杯-sort排序(上)

sort排序 🎈1.算法说明🎈2.例题🔭2.1例题一🔭2.2例题二🔭2.3例题三🔭2.4例题四🔭2.5例题五🔭2.6例题六 🎈1.算法说明 🔎对于一个数组,通过对数组中…

Spring Security 存储密码之 JDBC

Spring Security的JdbcDaoImpl实现了UserDetailsService接口,通过使用JDBC提供支持基于用户名和密码的身份验证。 JdbcUserDetailsManager扩展了JdbcDaoImpl,通过UserDetailsManager接口提供UserDetails的管理功能。 当Spring Security配置为接受用户名/密码进行身份验证时,…

南卡Neo2评测:实力诠释骨传导耳机全能旗舰,细节展现匠心之作

前段时间朋友让我帮他寻找一款佩戴舒适、音质体验好的蓝牙耳机,因为比较忙所以一直把这件事搁置了,刚好这两天比较闲,所以也是在综合个人的经验和目前较为热门的一些品牌款式,决定帮他寻找一款骨传导耳机,因为骨传导耳…

JVM-字节码应用

一、字节码的应用远超你的想象 二、ASM介绍与读取字节码实战 用CoreAPI解析和TreeAPI都能做字节码解析,区别,TreeAPI必须读取完整字节码信息,才能做解析。 下面代码,使用CoreAPI做解析: package asm;public class MyM…

[已解决]504 Gateway Time-out 网关超时

文章目录 问题:504 Gateway Time-out 504 Gateway Time-out 网关超时思路解决 问题:504 Gateway Time-out 504 Gateway Time-out 网关超时 思路 网上的常规思路是修改nginx配置文件,增加请求执行时间,试过没有用 keepalive_timeout 600; fastcgi_con…

JVM系列-7内存调优

👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家📕系列专栏:Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理🔥如果感觉博主的文…

案例分享 | 助力数字化转型:嘉为科技项目管理平台上线

嘉为科技项目管理平台(一期)基于易趋(EasyTrack)进行实施,通过近一年的开发及试运行,现已成功交付上线、推广使用,取得了良好的应用效果。 1.关于广州嘉为科技有限公司(以下简称嘉为…

龙芯,启动!

本文为小白从购买龙芯3A6000主板、硬件安装、软件安装的简单教程。 1 购买 目前(2024年1月)最新的龙芯主板采用龙芯处理器3A6000和7A2000桥片设计的DTX主板,CPU主频可达2.5GHz,2个DDR4内存插槽。桥片内部集成GPU,支…

如何把openwrt的ipk软件包安装到ubuntu上

前提:都是arm64的架构的软件包。 下载openwrt的ipk软件包 1. 从https://pkgs.org/ 查找下载软件包: 本文以swconfig软件包为例,下载swconfig和相关的依赖软件包: swconfig_12_aarch64_cortex-a72.ipk libuci20130104_2021-10-2…

podman+centos和docker+alpine中作性能对比遇到的问题及解决

1.dockeralpine中遇到这个问题 这是由于缺少相关的配置和依赖造成的 通过以下命令在alpine中安装相关配置 apk add --no-cache build-base cairo-dev cairo cairo-tools jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev 2.alpine中python找…

C++——IO流

C语言中我们学习过文件IO的相关函数,那么在C中也一定有各种IO流的 函数或者功能,由我今天来简单介绍一下C中IO流的大致原理及使用。在C语言中我们经常会使用到scanf、printf、sscanf、sprintf等等来实现进程和文件之间数据的流动,在C中虽然由…

python黑马模块

1、使用内置模块 # import通过.使用模块内部的全部功能 """ import time print("ff") time. sleep(5) print("as")# 使用from 导入某个功能 from time import sleep print("ff") sleep(5) print("as")# 使用 * 导入全部…

taskflow 源码阅读笔记-1

之前写了一篇介绍Taskflow的短文:传送门 Taskflow做那种有前后依赖关系的任务管理还是不错的,而且他的源码里运用了大量C17的写法,觉得还是非常值得学习的,因此决定看一下他的源码,这里顺便写了一篇代码学习笔记。 概…

Amazon 亚马逊新玩具——在线购物试衣服“虚拟试穿”模型:Diffuse to Choose

这个模型拥有强大的能力,它能够将任何商品无缝地融入任何环境之中,确保商品与环境完美匹配。例如,你可以轻松地将在线商店中的椅子图片放入你客厅照片中,预览它实际摆放的效果。无论环境如何变化,该模型都能确保商品展…

Java复习系列之阶段二:数据库

1. 基础语法 1.1 DQL(数据查询语句) 执行顺序: from、join 、on、where、group by、having、select、distinct、order by、limit 1.2 DML(数据修改语言) 对数据表的增删改 insert into update set delete form 1.…