java之并发&线程(二)
在并发量高一些的情况下。
if(a == 10){
a = 5;
}
假设a是一个实例变量而不是局部变量,此时就可能会产生问题。在一个线程调用 if 部分时,另一个线程将a的值修改了。这时在堆中读取到的a的值是修改后的值,导致程序结果不正确。
计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。
程序在运行时,会将数据从主存复制一份到CPU的高速缓存中,CPU就从高速缓存读取数据。计算后将高速缓存中最新的数据刷新回主存。
在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存
如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),那么就可能存在缓存不一致的问题。
当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
并发编程的三个问题:原子性,可见性,一致性。
原子性:一个操作或者多个操作 要么全部执行,要么就都不执行。 银行转账。
可见性:多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 i++
有序性:程序执行的顺序按照代码的先后顺序执行。
volatile这个关键字是具有有序性和可见性的。但是没有原子性。
有序性跟指令重排序联系在一起。什么是指令重排序呢。就是计算机觉得你这个顺序慢,它搞一个自己的顺序。这个顺序不会影响你的执行结果。这个就是指令重排序。目的就是减少中断次数。这个的缺点就是多线程环境下可能会因为个别值顺序执行靠后而产生相反的结果或者error。下面语句1,2,重排序。可能导致error.
//线程1:
context = loadContext();
//语句1
inited =
true
;
//语句2
//线程2:
while
(!inited ){
sleep()
}
doSomethingwithconfig(context);
官方一点:处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
CPU在执行指令的时候分几步。
取指译码执行访问存储器回显。等等。有了流水线技术,可以多条指令并行执行。这样大大降低了执行全部多条指令所需要的时间。但是流水线非常害怕中断。 指令重排序的目的就是尽量少的中断流水线。 IF-ID-EX-MEM-WB
而volatile这个关键字的一个作用就是禁止指令重排序。
也就是volatile这个关键字修饰的变量前面和后面的代码不能出轨。相对于volatile这个变量的位置。
//线程1
boolean
stop =
false
;
while
(!stop){
doSomething();
}
//线程2
stop =
true
;
第一:使用volatile关键字会强制将修改的值立即写入主存;
第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效
第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
所以上述代码不会出现死循环。也就是volatile保证了可见性。
但是volatile是不存在原子性的。原子性需要锁来保证。