线程安全之可见性、有序性以及原子性

  • 可见性:一个线程对主内存的修改可以及时的被其他线程观察到。
  • 有序性:一个线程观察其他线程中的指令执行顺序,由于指令 重排序的存在,该观察结果一般杂乱无序。
  • 原子性:提供了互斥访问。
特性操作
可见性可以由final(不会修改)、volatile(强制更新+读取主内存)以及synchronized(在unlock时会刷新所有已修改数据到主内存,lock时会从主内存重新加载数据)实现
有序性可以由volatile(禁止指令重排序)/synchronized(一个变量最多只能有一个线程对其lock)实现
原子性只有synchronized可以实现原子性,保证synchronized的代码块串行执行

synchronized在加锁时进行数据的重加载(主内存 -> 工作内存),在释放锁时将数据刷新到主内存。

冲突访问(Conflicting Accesses) 对同一个共享字段或数组元素存在两个访问(读或写),且至少有一个访问是写操作,就称作有冲突。当程序包含两个没有被 happens-before 关系排序的冲突访问时,就称存在数据争用。

动作A对于动作B是happen-before,即意味着动作A的修改(所有数据)对于动作B是可见的。

happens-before关系如下:

  1. 某个线程中的每个动作都 happens-before 该线程中该动作后面的动作(这里无论指令是否重排序都必须满足)。
  2. 某个管程 m 上的解锁动作 synchronizes-with 所有后续在 m 上的锁定动作(这里的后续是根据同步顺序定义的)。
  3. 对 volatile 变量 v 的写操作 synchronizes-with 所有后续任意线程对 v 的读操作(这里的后续是根据同步顺序定义的)。
  4. 用于启动一个线程的动作(start方法) synchronizes-with 该新启动线程中的第一个动作。
  5. 初始化动作对于其他线程的第一个调用是happen-before
  6. happen-before是闭包传递的。

数据争用的例子,左是正常的访问顺序,右是数据争用,由于存在错误的数据流,所以是一个错误的同步。
这里写图片描述

一个程序不存在数据争用,那么该程序是顺序一致的,即该程序不存在可见性问题。但是该程序还是可能在原子访问上出现问题,所以不存在数据争用并不是意味着线程安全,只有加上原子性保证才是线程安全的。这也是为什么synchronized是线程安全的,而volatile(保证所有变量访问都是happen-before)只有在所有操作都是原子的情况下才是线程安全的原因。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值