没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
java 线程-Java 内存模型
多线程编程 bug 源头
cpu,内存,I/O 设备都在不断的迭代,不断朝着更快的方向努力,但是,
在这个快速发展的过程中,有一个核心矛盾一直存在,就是这三者的
速度差异。cpu > 内存 > i/0。根据木桶理论,程序整体的性能取决于
最慢的操作-i/o 设备的读写,也就是说单方面提高 cpu 性能是无效的。
为了平衡这三者的速度差异,计算机体系机构,操作系统,编译程序
都做出了贡献,主要体现在:
1. cpu 增加了缓存,以均衡与内存的速度差异;
2. 操作系统增加了进程,线程,以分时复用 cpu,进而均衡 cpu 与
i/o 设备的速度差异;
3. 编译程序优化指令执行次序,使得缓存能够得到更加合理地利
用。
CPU 缓存-可见性问题
在单核时代,所有的线程都在一颗 cpu 上运行,cpu 缓存与内存的数
据一致性容易解决,因为所有线程都操作同一颗 cpu 的缓存,一个线
程对缓存的写,对另外一个线程来说一定是可见的。 一个线程对共
享变量的修改,另外一个线程能够立刻看到,我们称之为可见性。
在多核时代,每颗 cpu 都有自己的缓存,这时 cpu 缓存与内存的数据
一致性就没那么容易解决了。当多个线程在不同的 cpu 上运行时,这
些线程操作的是不同的 cpu 缓存。假设线程 a 在 cpu-1 上运行,线程 b
在 CPU-2 上运行,此时线程 a 对变量 v 的操作对线程 b 来说是不可见
的。
public class Test{
private long count = 0;
private void add(){
int i = 0;
while(i++ <= 10000){
count += 1;
}
}
public static long calc(){
final Test test = new Test();
Thread t1 = new Thread(()->{
test.add();
});
Thread t2 = new Thread(()->{
test.add(); });
t1.start();
t2.start();
t1.join();
t2.join();
retun count;
}
}
上面的程序,如果在单核时代,那么结果毋庸置疑是 20000,但在多
核时代,最后结果是 10000-20000 之间的随机数。 我们假设 t1 和 t2
线程同时开始执行,那么第一次都会将 count=0 读到各自的 cpu 缓存
里,执行完 count += 1 之后,各自的 cpu 缓存里的值都是 1,而不是
我们期望的 2.之后由于各自的 cpu 缓存里都有 count 值,所以导致最
终 count 的计算结果小于 20000.这就是缓存的可见性问题。
线程切换-原子性问题
java 并发程序都是基于多线程的,这样也会涉及到线程切换。执行
count += 1 的操作,至少需要三条 cpu 指令:
1. 首先,需要把变量 count 从内存中加载到 cpu 的寄存器。
2. 在寄存器中执行 +1 操作。
3. 将结果写入内存,缓存机制导致可能写入的是 cpu 的缓存还不
是内存。
对于上面的三个指令来说,如果线程 a 刚刚执行完指令 1,就和线程
b 发生了线程切换,导致 count 的值还是为 0,而不是+1 之后的值,
就会导致其结果不是我们希望的 2.
我们把一个或者多个操作在 cpu 执行的过程中不被中断的特性称为
原子性。
剩余21页未读,继续阅读
资源评论
Andy&lin
- 粉丝: 97
- 资源: 214
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功