线程安全是什么

发布时间:2021-06-29 16:18:22 作者:Leah
来源:亿速云 阅读:177

今天就跟大家聊聊有关线程安全是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

内容大纲

线程安全是什么

小故事

带薪蹲坑,相信都是大伙都爱做的事情,阿星也不例外,但是我司所在的楼层的坑位较少,粥少僧多,十分烦恼。

阿星(线程A)每次去厕所(共享资源),门都是锁着的,说明有同事在里面占着坑(线程B持有锁),只能无奈的在外面乖乖的等着,不久后冲水声响起,同事爽完出来(线程B释放锁),阿星一个健步进入厕所把门锁住(线程A持有锁),享受属于自己的空间,晚来的其他同事只能乖乖排队,一切都是那么井然有序。

假设门锁坏了,井然有序就不存在了,上厕所不再是享受,而是高度紧张,防止门突然被打开,更糟糕的是,开门时,是个妹子,这下不仅仅是线程安全问题,还有数组越界了。

故事说完,扯了那么多,就是想说明,在多线程环境里,对共享资源进行操作,如果多线程之间不做合理的协作(互斥与同步),那么一定会发生翻车现场。

竞争条件

因为多线程共享进程资源,在操作系统调度进程内的多线程时,必然会出现多线程竞争共享资源问题,如果不采取有效的措施,则会造成共享资源的混乱!

线程安全是什么

来写个小例子,创建两个线程,它们分别对共享变量 i 自增 1 执行 1000 次,如下代码

线程安全是什么

正常来说,i 变量最后的值是 2000 ,可是并非如此,我们执行下代码看看结果

运行了两次,结果分别是1855、2000,我们发现每次运行的结果不同,这在计算机里是不能容忍的,虽然是小概率出现的错误,但是小概率它一定是会发生的。

汇编指令

为了搞明白到底发生了什么事情,我们必须要了解汇编指令执行,以 i1 为例子,汇编指令的执行过程如下

线程安全是什么

好家伙,一个加法动作,在 C P U 运行,实际要执行 3 条指令。

现在模拟下线程A与线程B的运行,假设此时内存变量 i 的值是 0,线程A加载内存的 i 值到寄存器,对寄存器 i 值加 1,此时 i 值是 1,正准备执行下一步寄存器 i 值回写内存,时间片使用完了,发生线程上下文切换,保存线程的私有信息到线程控制块T C P。

操作系统调度线程B执行,此时的内存变量 i 依然还是 0,线程B执行与线程A一样的步骤,它很幸运,在时间片使用完前,执行完了加 1,最终回写内存,内存变量 i 值是 1

线程B时间片使用完后,发生线程上下文切换,回到线程A上次的状态继续执行,寄存器中的 i 值回写内存,内存变量再次被设置成 1

按理说,最后的 i 值应该是 2,但是由于不可控的调度,导致最后 i 值是 1,下面是线程A与线程B的流程图

线程安全是什么

小结

这种情况称为竞争条件(race condition),多线程相互竞争操作共享资源时,由于运气不好,在执行过程中发生线程上下文切换,最后得到错误的结果,事实上,每次运行都可能得到不同的结果,因此输出的结果存在不确定性(indeterminate)。

互斥与同步

为了解决因竞争条件出现的线程安全,操作系统是通过互斥与同步来解决此类问题。

互斥概念

多线程执行共享变量的这段代码可能会导致竞争状态,因此我们将此段代码称为临界区(critical section),它是执行共享资源的代码片段,一定不能给多线程同时执行。

所以我们希望这段代码是互斥(mutualexclusion)的,也就说执行临界区(critical section)代码段的只能有一个线程,其他线程阻塞等待,达到排队效果。

线程安全是什么

互斥并不只是针对多线程的竞争条件,同时还可用于多进程,避免共享资源混乱。

同步概念

互斥解决了「多进程/线程」对临界区使用的问题,但是它没有解决「多进程/线程」协同工作的问题

我们都知道在多线程里,每个线程一定是顺序执行的,它们各自独立,以不可预知的速度向前推进,但有时候我们希望多个线程能密切合作,以实现一个共同的任务。

所谓同步,就是「多进程/线程间」在一些关键点上可能需要互相等待与互通消息,这种相互制约的等待与互通信息称为「进程/线程」同步。

举个例,有两个角色分别是研发、质量管控,质量管控测试功能,需要等研「发完成开发」,研发要修bug也要等质量管控「测试完成提交B U G」,正常流程是研发完成开发,通知质量管控进行测试,质量管控测试完成,通知研发人员修复bug。

线程安全是什么

互斥与同步的区别

显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要按照某种次序来运行相应的线程(也是一种互斥)! 

互斥与同步的实现

互斥与同步可以保证「多进程/线程间正确协作」 ,但是互斥与同步仅仅只是概念,操作系统必须要提供对应的实现,针对互斥与同步的实现有下面两种

这两个种方式都可以实现「多进程/线程」互斥,信号量比锁的功能更强一些,它还可以方便地实现「多进程/线程」同步。

顾名思义,给临界区上一把锁,任何进入临界区)的线程,必须先执行加锁操作,加锁成功,才能进入临界区,在离开临界区时再释放锁,达到互斥的效果。

线程安全是什么

锁的实现方式又分为「忙等待锁」和「无忙等待锁」

忙等锁

检查并设置(test-and-set-lock,TSL)是一种不可中断的原子运算,它属于原子操作指令,可以通过它来实现忙等锁(自旋锁)。

test-and-set-lock 指令伪代码

线程安全是什么

检查并设置做了如下几个步骤

上面的步骤,把它看成一步并具备原子性,原子性的意思是指全部执行或都不执行,不会出现执行到一半的中间状态.

伪代码testAndSetLock实现忙等锁(自旋锁)

线程安全是什么

下面两种场景运行

当获取不到锁时,线程就会一直 wile 循环,不做任何事情,所以就被称为忙等待锁,也被称为自旋锁。

这是最简单的锁,一直自旋,利用 C P U 周期,直到锁可用。在单处理器上,需要抢占式的调度器(即不断通过时钟中断一个线程,运行其他线程)。否则,自旋锁在 C P U 上无法使用,因为一个自旋的线程永远不会放弃 C P U。

无忙等锁

顾名思义,无忙等锁不需要主动自旋,被动等待唤醒即可,在没有获取到锁的时候,就把该线程加入到等待队列,让出 C P U 给其他线程,其他线程释放锁时,再从等待队列唤醒该线程。

线程安全是什么

两种锁的实现都是基于检查并设置(test-and-set-lock,TSL),上面只是简单的伪代码,实际上操作系统的实现会更复杂,但是基本思想与大致流程还是与本例一样。

信号量

操作系统中协调「多线程/进程」共同配合工作,就是通过信号量实现的,通常信号量代表「资源数量」,对应一个整型(s e n)变量,还有两个原子操作的系统调用函数来控制「资源数量」。

P V操作必须是成对出现,但是没有顺序要求,也就说你可以P V或V P。

举个例子,最近新冠病毒又出来捣乱了,为了自身安全,大家都去打疫苗,因为医生只有两位(相当于2个资源的信号量),所以同时只能为两个人接种疫苗,过程如下图

线程安全是什么

使用伪代码实现P V 信号量

线程安全是什么

P V操作的函数是由操作系统管理和实现的,所以 P V 函数是具有原子性的。

实践

信号量还是比较有意思的,这里来做几个实践,加深大家对信号量的理解,实践的内容分别是

互斥

使用信号量实现互斥非常简单,信号量数量为1,线程进入临界区进行 P 操作,离开临界区进行 V 操作。

线程安全是什么

事件同步

以前面说的研发、质量管控线程为例子,实现事件同步的效果,伪代码如下

线程安全是什么

首先抽象出两个信号量,「是否能提测」与「是否能修BUG」,它们默认都是否,也就是 0,关键点就是对两个信号量进行 P V 操作

生产者与消费者

生产者与消费者是一个比较经典的线程同步问题,我们先分析下有那些角色

线程安全是什么

问题分析可以得出:

通过问题分析我们可以抽象出3个信号量

伪代码如下

线程安全是什么

关键的 P V 操作如下

看完上述内容,你们对线程安全是什么有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

推荐阅读:
  1. java中线程安全是什么意思
  2. Java线程安全与非线程安全解析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

线程安全

上一篇:公有云、公有云和混合云是什么

下一篇:JVM调优是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》