线程安全性的基本概念

2023-10-26

线程安全性

      我们总是说要编写线程安全的代码,有时候也会讨论某个类是不是线程安全的。那到底什么是线程安全性呢?
      网上有很多说法:可以被多个线程调用,并且线程之间不会出现错误的交互;
多个线程调用时,不需要做额外的动作等等。
但这话,明明什么都说了,又好像什么都没有说。到底怎么才能在多个线程之间安全地调用呢,怎么算安全呢?

正确性

      在线程安全性定义中,最核心的概念就是正确性。如果对线程安全性的一定是模糊的,那么就是缺乏对正确性的清晰定义。
    正确性的含义是:这个类的行为与其规范完全一致。我们通常不会为一个类编写详细的规范,但是这并不妨碍我们对正确性的理解,在写一个类、一个方法的时候,对于这个类的作用,这个方法的执行我们是有一个预期结果的。如果在多线程环境下,多个线程都在调用的时候,这个类这个方法仍然能始终保持预期的行为与结果,就是符合正确性的。在对正确性有一个比较清楚的认知之后,就可以定义线程安全性了:

当多个线程访问某个类时,不管这些线程如何交替执行,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的

对共享和可变的状态访问操作进行管理

      那么如何做才能让类在多线程的环境下始终表现正确呢,换句话说,多线程环境下如何避免类出现不可预知,预料之外的行为。核心在于:要对状态访问操作进行管理,特别是对共享的和可变的状态的访问。
      对象的状态指的是存储在状态变量(例如实例或者静态域、成员变量)中的数据。对象的状态可能包含依赖的其它对象的状态。例如HashMap的状态不仅仅存储在HashMap本身,还存储许多Map.Entry对象中。在对象的状态中包含了任何可能影响其外部可见行为的数据。
      共享意味着可以由多个线程同时访问、可变则意味这变量的值在整个生命周期中可以发生改变。当多个线程访问某个状态变量,并且至少有一个线程会修改变量的值时,就需要采用同步机制来协调这些线程对对象的访问。这里说的”同步机制“包括:synchronized关键字、volatile变量、显式锁、ThreadLoacl变量以及原子变量。

当多个线程同时访问一个可变的状态变量时,如果没有采用合适的同步,程序会出现错误。有3中方式可以解决这个问题。
      1.不在线程之间共享该状态变量
      2. 将状态变量修改为不可变变量
      3.在变量访问时采用合适的同步

原子性、可见性、有序性

      经常看到一种说法,叫”多线程的三个特性:原子性、可见性、有序性“。什么意思?多线程跟这三个东西有什么关系?
      按照我的理解,原子性、可见性和有序性是代码执行的特征,为什么许多单线程下正常运行的代码跑的好好的,一旦多个线程同时访问就会出现莫名其妙的问题!恰恰是因为多线程破坏了这3个规则,在单线程环境下i++操作是原子的,但是到了多线程访问时,它就不是原子性了。多线程编程时我们为什么要加锁,要给变量加volatile关键词,要采取各种同步机制,就是需要保证不破坏原子性、可见性,保证有序性。

原子性:

一个操作或者多个操作,要么全部执行并且执行过程中不会被任何因素打断,要么不执行。

  1. 竞态条件
    当某个计算的正确性,取决与多个线程交替执行的时序时,就会发生竞态条件。典型的竞态条件就是:先检查后更新,本质上是:基于一种可能失效的观察结果来做出判断或者执行操作
    例如我们常见的 i++操作,其实包含了3个操作。先获取i的值,将i的值加1,将加1后的值写入变量i。
    如果没有进行合适的同步(将上面3个操作整合为原子操作),多个线程同时执行会得到不符合预期的结果:可能两个线程都得到2。
    2.复合操作
    有时候我们需要一组操作以原子方式执行,在执行过程中要防止其它线程使用这个变量。只能允许其它线程在复合操作执行前或者执行后访问状态变量,而不是在修改的过程中。

可见性

多个线程同时访问一个共享变量时,某个线程修改了共享变量,其它线程能够立即获取到修改后的值

有序性

程序的执行顺序按照代码的先后顺序执行
由于指令重排等虚拟机优化策略,可能会导致程序的执行顺序并不严格按照代码的顺序。
多线程就是需要保证这3个特性的正常执行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程安全性的基本概念 的相关文章

随机推荐

  • 基于js的炫酷动画的代码

  • 提领类型双关的指针将破坏重叠规则——strict-aliasing

    转载请保留本行原始出处声明信息 http www zeali net entry 454 MaDe1nZEAL warning dereferencing type punned pointer will break strict alia
  • 怎么理解离散数据?

    离散数据是指数据值只能取有限的 可数的数值 而不能取到连续的数值 相对的 连续数据可以取到任意数值 离散数据是在一个固定的集合中取值 集合通常是整数集或有限的枚举集 例如 一个人的家庭成员数量就是一个离散数据 它只能取到整数值 如0 1 2
  • JS的类型转换和float取n位小数

    javascript中的变量都是弱类型 所有的变量都声明为var 在类型转换过程中就没有java那么方便 它是通过 parseInt 变量 parseFloat 变量 等方法来进行类型转换的 注意 没有parseDouble 变量 这种类型
  • k8s deployment Strategy 更新策略

    k8s更新策略 https kubernetes io zh docs concepts workloads controllers deployment Strategy spec strategy specifies the strat
  • vue修改富文本中的元素样式

    富文本编辑器目前应用很广泛 而有时候我们想要对其中的一些元素的样式进行修改 就会遇到问题 首先 直接修改是不可行的 因为是用v html标签进行渲染的 无法直接获取到 在修改的时候 一般是按标签进行修改 当然 也可以按class和id等 这
  • python:从键盘输入学生的成绩,转换成 5 个等级输出。A(90~100),B(80~89),C(70~79),D(60~69),E(0~59)。试用嵌套分支结构实现。

    grades eval input 请输入你的成绩 if 90 lt grades lt 100 print A elif 80 lt grades lt 90 print B elif 70 lt grades lt 79 print C
  • 前置机

    前置机这个概念一般在银行 券商 电信运营商那里用的比较多 这些地方都有很多后台核心处理系统 对外提供各种接口服务 如果我有某种业务接口需要跟他们的后台系统打交道 要从我们的外部网络访问他们的后台系统 这些单位是绝对不允许的 这个时候 他们要
  • Unity中协程与线程的区别

    本文转载自 https blog csdn net qq 25122429 article details 80481443 协同程序 coroutine 与多线程情况下的线程比较类似 有自己的堆栈 自己的局部变量 有自己的指令指针 IP
  • 【编程之路】面试必刷TOP101:贪心算法(95-96,Python实现)

    面试必刷TOP101 贪心算法 95 96 Python实现 95 分糖果问题 小试牛刀 95 1 贪心思想 要想分出最少的糖果 利用贪心思想 肯定是相邻位置没有增加的情况下 大家都分到1 相邻位置有增加的情况下 分到糖果数加1就好 什么情
  • Java-内部类

    Java 内部类 1 概念 Java中允许将一个类A声明在另外一个类B中 则类A就是内部类 类B 就称为外部类 内部类的分类 成员内部类 一方面作为外部类的成员 可以调用外部类的结构 可以被static修饰 可以被四种不同的权限修饰符 pu
  • 在手机上运行Python--安卓linux终端Termux

    今天突发奇想 想找一种在手机上运行Python的工具 于是发现了这个安卓端的linux终端 Termux 可以在手机上实现一个微型的linux终端 网上已经有不少教程了 我在这里做一下汇总 1 安装Python以及常用的package nu
  • Python+OpenCV3简单手势识别

    文章目录 安装相关库 原理简述 代码 效果实现 今天教大家一个有趣的玩法 如何利用Python opencv3实现简单的手势识别 当然网上也有相关教程 但绝大多数给出的代码拿来之后你是不能直接用的 这对于拿来主义的同学来说简直太 禽兽 了
  • python rpy2_Python&R语言-rpy2使用示例

    前言 Python编程灵活方便 R的模型方法众多 如何将两者结合起来 发挥更大的作用 值得探索 Python可以直接调用R的函数 R是开源项目 肯定会有一些第三方库实现Python与R互通 需要在python中调用R 实在是一种无奈的选择
  • vue3+TSX+element-plus(DateTimePicker)做一个时间范围选择器

    element plus包括element ui支持时间范围选择 把type指定成datetimerange就行了 但是它不支持单个选择 也许unlink panels这个配置有用 但我是用TSX写的 传了个true进去没用 怎么试都不行
  • 20张图带你了解JVM运行时数据区(上)

    我们的JVM系列已经断更好几天了 小伙伴们在后台疯狂私信阿Q 想看后续内容 今天它来了 相信大家在上篇文章中已经对类加载子系统有了清晰的认识 接下来就让我们来揭开 运行时数据区 的神秘面纱吧 运行时数据区总览 内存是非常重要的系统资源 是硬
  • GC overhead limit exceeded问题

    Java运行时环境内置了 垃圾收集 GC 模块 上一代的很多编程语言中并没有自动内存回收机制 需要程序员手工编写代码来进行内存分配和释放 以重复利用堆内存 在Java程序中 只需要关心内存分配就行 如果某块内存不再使用 垃圾收集 Garba
  • vue watermark水印添加

    vue 水印实现 Vue项目在页面添加水印功能 不允许操作dom关闭水印 1 添加watermark dom插件 npm i watermark dom save 引用 watermark dom import watermark from
  • lsqcurvefit函数的基本用法

    本文讲解lsqcurvefit函数的基本用法 一 lsqcurvefit函数的简单使用格式 x lsqcurvefit fun x0 xdata ydata x resnorm lsqcurvefit fun x0 xdata ydata
  • 线程安全性的基本概念

    线程安全性 我们总是说要编写线程安全的代码 有时候也会讨论某个类是不是线程安全的 那到底什么是线程安全性呢 网上有很多说法 可以被多个线程调用 并且线程之间不会出现错误的交互 多个线程调用时 不需要做额外的动作等等 但这话 明明什么都说了