并发编程三大特性
原子性
一个操作或者多次操作,要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。
对于基本数据类型的访问,读写都是原子性的【long和double可能例外】。
如果需要更大范围的原子性保证,可以使用synchronized关键字满足。
可见性
当一个变量对共享变量进行了修改,另外的线程都能立即看到修改后的最新值。
volatile
保证共享变量可见性,除此之外,synchronized
和final
都可以 实现可见性。
synchronized
:对一个变量执行unclock之前,必须先把此变量同步回主内存中。
final
:被final修饰的字段在构造器中一旦被初始化完成,并且构造器没有把this的引用传递出去,其他线程中就能够看见final字段的值。
有序性
即程序执行的顺序按照代码的先后顺序执行【由于指令重排序的存在,Java 在编译器以及运行期间对输入代码进行优化,代码的执行顺序未必就是编写代码时候的顺序】,volatile
通过禁止指令重排序保证有序性,除此之外,synchronized
关键字也可以保证有序性,由【一个变量在同一时刻只允许一条线程对其进行lock操作】这条规则获得。
CPU缓存模型是什么
高速缓存为何出现?
计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。
为了解决CPU处理速度和内存不匹配的问题,CPU Cache出现了。
图源:JavaGuide
缓存一致性问题
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
在单线程中运行是没有任何问题的,但是在多线程环境下问题就会显现。举个简单的例子,如下面这段代码:
i = i + 1;
按照上面分析,主要分为如下几步:
- 从主存读取i的值,复制一份到高速缓存中。
- CPU执行执行执行对i进行加1操作,将数据写入高速缓存。
- 运算结束后,将高速缓存中的数据刷新到内存中。
多线程环境下,可能出现什么现象呢?
- 初始时,两个线程分别读取i的值,存入各自所在的CPU高速缓存中。
- 线程T1
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/javal-zj/6620.html