赞
踩
跨线程共享内存 - 章节
介绍跨线程共享内存 - 小节
线程间的沟通
线程需要共享它们的数据
比让值可见更复杂
值也必须正确:
1> 缓存[读取陈旧值]的问题
2> 编译器“欺骗”问题
好的设计是关键
意识到这一点很重要
1> 需要在设计阶段考虑如何共享数据
2> 没有计划地编写代码会导致困难
Bugs可以是:
1> 微妙的
2> 很难找到
3> 只是偶尔出现
将收获哪些...
不正确地同步数据的问题涵盖在四个例子中
如何安全地将不可变对象发布到线程
同步化在Java内存模型中得到了保证
1> java语言规范(第17章)
线程安全和java内存模型 - 小节
线程安全的
代码在多线程环境中正确执行
什么是共享状态?
程序数据
1> 已经存储
2> 可以更新
潜在的可共享:
1> 对其他线程可见
2> 静态变量、类数据、组合类数据、数组项、集合项
3> 不是局部变量
公布的数据
向其他线程公开的数据
必须遵循Java内存模型中的指导原则
1> 或者承担后果[微小的bug]
数据发布正确吗?
最后区域安全吗?
1> 是的,但请稍后注意
知道的数据正确地发布
1> 需要检查程序
2> 引用中包含的所有数据必须正确地发布
Java内存模型
听到这个定义:
1> 这会让你感到困惑吗?
Java内存模型
"Java内存模型描述了Java编程语言中的线程如何通过内存进行交互。"
——维基百科
‘The Java Memory Model defines a set of guarantees which, when applied to a program, ensure memory interactions between threads occur in a specified deterministic fashion.’
数据同步
当我们在一个线程上写一个值时
1> 我们想知道这个值在被另一个读取时是否正确可见
数据正确同步的
1> 意味着正确的可见性
不要与synchronized关键字混淆
示例:
共享非同步数据的问题示例 - 小节
伪代码约定
例如:
1> L1 - 表示局部变量L1
2> S2 - 表示共享变量S2
3> S2.X - S2的X字段
4> 1.2 - 线程1,语句2
变量以默认值开始
执行顺序
在单线程代码中,只有一个
在多线程代码中,执行顺序取决于:
1> 调度器
2> 处理器
3> 线程之间的相互作用
示例2:
代码重新排序
编译器,JVM或处理器可以重新排序代码
1> 让它执行得更快
单线程时不要注意它
1> 除非使用调试器
多线程会有害吗
优化是特定于平台的
Bug可能只是偶尔出现在生产系统上
尽管进行了广泛的开发测试
在行动中捕捉Bug
尝试使用调试器进行日志记录或单步执行?
但这种影响:
1> 执行顺序,时间,缓存内容,优化
打印更改线程所做的观察
1> Bug可能会消失
Heisenbugs
很难检测到
导致奇怪的bug报告
当你试图观察它们时,它们就消失了
使用Volatile解决数据同步问题 - 小节
数据竞争
不同的执行顺序是可能的
1> 不知道会发生什么
当读取未同步的共享可变数据导致意外或不正确的值时,我们有一场数据竞争。
数据同步
将volatile关键字添加到共享变量定义中
volatile有几个影响,在java面试中会被问到
在c/c++上使用volatile(但不是java!)
不缓存
在多线程条件下没有保证
在Java中的Volatile
Volatile函数一:
任何线程读取都会看到最新值
1> 机制未指定
Volatile变量
在伪代码中使用v表示volatile
例如vS1 - 共享变量S1是不稳定的
但最后不能是volatile
数组和对象引用可以标记为volatile
1> 不影响内容
Voletaile阻止优化
Volatile函数2:
1> 表示值可以在线程之间共享
2> 阻止基于程序顺序的优化
Voletile和内存关卡
Volatile函数3:
在线程之间同步数据
1> 通过安装内存关卡
内存关卡的定义
一种屏障指令,使中央处理器(CPU)或编译器对屏障指令前后发出的内存操作强制执行顺序约束
内存关卡的效果
一个volatile变量的写入器的内存状态是可见的
必须至少对相同变量的任何读者可见
至少指在写的时间点或以后的时间点
1> 很难/不可能准确地推断出处于什么状态
在Java中的Volatile
Volatile函数4:
防止某些重新排序(如下)
代码重新排序的可能性
改编自JSR-133烹饪书的编译器作家由道格李
Inconsistencies and Broken Invariants
状态,我们可以看到一个volatile读取:
1> 在前一个volatile write_or_later
可能只意味着更新对象的某些字段
1> Inconsistent/broken invariants
解决方案:
1> 正确发布对象
2> 互斥锁
Volatile的缺点
影响性能
1> 最新值必须对其他核心可见
不保证数据一致性
发布对象以共享数据 - 小节
发布对象
一个线程执行更新
1> 发布对其他线程的更改
2> 其他线程使用更新后的数据
最后一个变量
如果对象发布正确
- Reference to 'this'not allowed to escape during construction
其他线程只会看到初始化的最终值
- 不需要担心数据竞争
发布不可变对象比同意不更改它们要好
Publishing and Libraries
问题:
- 多线程行为的文档?
- 源代码可用?
- 可能在读取操作时在引擎盖下发生突变
- 实现随着升级而变化,建议存储结果而不是发布库对象
Publishing in Practice
它的工作原理,但是……
- 需要好的设计
- 清楚/简单/严格的工作实践
- 开发人员知识渊博的
- 照顾了
比总是检查同步要好
其他Java内存模型保证 - 小节
线程创建/死亡的保证
创建线程时
- 它至少可以看到状态创建者在创建时可以看到
当线程已死,而另一个线程调用iaAlive()或join()时
- 至少可以看到线程在退出时看到的状态
Word Tearing
数组的修改在哪里影响相邻的元素
如:
- 给定一个字节数组
- 如果处理器必须写完整的单词
- 相邻元素可能受到同步问题的影响
在Java中不是问题
64 Bit Primitives
长/双线程不安全(除非易挥发-volatile)
- 可分为两个32位操作读取/写入
- 因此可以读取从未赋值的值(只执行一个操作)
或者使用AtomicLong、Long或Double包装器
64位引用总是安全的
跨线程共享内存的摘要 - 小节
Thread Safety,Synchronization and Data Races
由于缓存、编译器和处理器优化
- 在单线程模式下工作的代码不能在多线程条件下工作
- 其中共享可变状态
- 需要正确同步,以避免数据竞争
线程安全、同步和数据竞争
标记变量volatile的作用:
- 随时查看最新版本
- 防止有害的优化
- 安装限制重新排序的内存栅栏
- 同步线程的‘世界观'
如何安全地发布对象
JMM担保:
- 线程的创建
- 线程死亡
- No word tearing
64位原始问题
- 引用是安全的
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。