5. 竞态条件与临界区

在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。如,同一内存区(变量,数组,或对象)、系统(数据库,web services等)或文件。实际上,这些问题只有在一或多个线程向这些资源做了写操作时才有可能发生,只要资源没有发生变化,多个线程读取相同的资源就是安全的。
多线程同时执行下面的代码可能会出错:

1 public class Counter {
2     protected long count = 0;
3     public void add(long value){
4         this.count = this.count + value;  
5     }
6 }

想象下线程A和B同时执行同一个Counter对象的add()方法,我们无法知道操作系统何时会在两个线程之间切换。JVM并不是将这段代码视为单条指令来执行的,而是按照下面的顺序:

  • 从内存获取 this.count 的值放到寄存器
  • 将寄存器中的值增加value
  • 将寄存器中的值写回内存

观察线程A和B交错执行会发生什么:

  • this.count = 0;
  • A: 读取 this.count 到一个寄存器 (0)
  • B: 读取 this.count 到一个寄存器 (0)
  • B: 将寄存器的值加2
  • B: 回写寄存器值(2)到内存. this.count 现在等于 2
  • A: 将寄存器的值加3
  • A: 回写寄存器值(3)到内存. this.count 现在等于 3

两个线程分别加了2和3到count变量上,两个线程执行结束后count变量的值应该等于5。然而由于两个线程是交叉执行的,两个线程从内存中读出的初始值都是0。然后各自加了2和3,并分别写回内存。最终的值并不是期望的5,而是最后写回内存的那个线程的值,上面例子中最后写回内存的是线程A,但实际中也可能是线程B。如果没有采用合适的同步机制,线程间的交叉执行情况就无法预料。

竞态条件 & 临界区

当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。 上例中add()方法就是一个临界区,它会产生竞态条件。在临界区中使用适当的同步就可以避免竞态条件。

临界区的代码访问,我们需要的是互斥。
互斥是这个资源只有我用,你不许用;
原子是我一口气把活干完,不间断;
根本就是两码事吧,即便起到的效果是一样的,也不能说表达的是同一个意思
a=a+1不是原子的,只有对除long和double外的基本类型进行简单的赋值或返回操作,才属于原子的。比如a=1;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值