主内存、工作内存
- 一条线程对应一个工作内存(专属高速缓冲)
- 多个工作内存对应一个主内存
- 通常情况下,主内存就是
Java Heap
,工作内存即虚拟机栈 - 工作内存是主内存的数据的拷贝
- 工作内存与主内存的交互是有一套规定的,有一套函数
- 线程不会直接从主内存中获取数据,而是从自己的工作内存中拿数据,拿不到就从主内存中加载到工作内存,在从工作内存中获取
Volatile
想当年这东西可是背过的,但是又何曾理解他到底是干嘛的呢,借此机会来了解一哈。
记得当时看到的说法是用Volatile
修饰的变量可以并发,不用怕同步问题,然而并不是这样的,但是他在某些情况下确实可以保证同步性,下面就来看看是咋整的。
被Volatile
修饰的变量有下面两个特点:
1. 可见性
一条线程修改了变量的值,其他使用它的线程能在使用它的时候获取到最新的值
volatile
修饰的变量在值改变后会立刻向主内存写入,以至于别的线程在读这个变量的时候会读到最新的值,(在这里,线程每次使用这个变量的时候都必须从主内存中更新最新的值)但是这里说的改变是指该操作是具有原子性的情况下,不具有原子性的操作,例如i++
,就算i
被vloatile
修饰那在多个线程中对它进行读写也是不安全的
2. 禁止指令重排序
大家知道JVM
是会对对字节码进行指令重排序优化的,也就是说,在一条线程内,我们的代码看起来是顺序执行的,然而实际上不是的,当上写文没有影响的时候,代码不一定是顺序执行的(先行的概念)
。在多条线程中看,我们的代码并不是顺序执行的,,一个线程的代码会被其他线程插入,但是,在线程内看还是顺序的,这就是相对顺序。那么volatile
修饰的变量,就禁止做这样的一种优化,在对volatile
修饰的变量赋值时,JVM
会加上一个lock
指令,禁止后面的指令重排序到lock
之前
Java内存模型欲实现
原子性
具有原子性的代码块执行时不会被打断
在并发中,我们的很多代码块是需要具有原子性的,JVM
则提供了synchronized
关键字让我们来实现代码原子性
可见性
一条线程修改了变量的值,其他使用它的线程能在使用它的时候获取到最新的值
上面说过,volatile
可以实现可见性,当然synchronized
也可以
有序性
线程之间的操作具有顺序,串行,线程之间的代码可以按照设想的顺序执行,不出现指令重排打乱顺序的情况
同样,synchronized
可以实现有序性,lock住了那都具有原子性,也无法重排序,别人插不进来
Volatile
也行,上面说过的,也是有lock的操作
先行发生
书中定义:先行发生是在Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,那么操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、调用了方法等
这是咋回事呢?来举个例子,假设一条线程中:操作A是i++;
,操作B是i--;
,若A先行发生于B,那么,从A到B一顿操作之后,i应该是原来的值,也就是说,B中观察到的i
是A操作完之后的值,这样就叫做能被观察到。
从上面的例子来看,貌似,先行发生=先发生,事实上并不是,再来举个例子,还是在一条线程中,操作A是i++;
,操作B是j++;
,A写在B的前面,AB挨着写,那么A是先行发生于B的,但是!!A并不一定比B先发生,在这种情况下,即B中没有用到A中的值的情况下,B无法感知A操作产生的影响,所以,JVM会对指令进行重排序优化,可能B先发生于A,也有可能不变。由于B没用到A中的值,所以不存在观不观察,没有破坏先行发生的规则
网友评论