一、变量分为哪几类
全局变量 = 属性(静态的、非静态的)局部变量 = 本地变量、参数
二、多线程间共享数据
全局变量:静态变量或共享对象
三、并发线程能不能看到共享变量的最新值,这就是并发中变量可见性问题
- 为什么不可见?
- 怎样才能可见
使用synchroized关键字,对线程主体进行包装
使用volatile关键字修饰共享变量
四、JAVA内存模型及操作规范
- 共享变量必须存放在主内存。
- 线程有自己的工作内存,线程只可操作自己的工作内存。
- 线程要操作共享变量,需从主内存中读取变量值到工作内存,改变后再从工作内存同步到主内存。
五、JAVA内存模型带来的问题
-
数据存在多份拷贝,导致多线程同时读写同一变量时,存在数据不准确的问题。即线程安全问题。
-
所以要使用线程同步或锁处理。
六、变量在线程1中更改,在线程2中能看到该变量的最新值
-
线程1修改A后,必须马上同步回主内存
-
线程2使用A前,需要重新从主内存读取到工作内存
七、JAVA内存模型-同步交互协议,规定了8种原子操作
- lock(锁定) 将主内存中的变量锁定,为一个线程所独占。
- unlock(解锁) 将lock加的锁解除,允许其它线程访问主内存中的该变量。
- read(读取) 作用于主内存变量,将主内存中的变量值读取到工作内存(从主内存读取到寄存器)。
- load(载入) 作用于工作内存变量,将read读取到的值保存到工作内存中的变量副本中(寄存器变量载入到工作内存)。
- use(使用) 作用于工作内存变量,将值传递给线程的代码执行引擎。
- assign(赋值) 作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本。
- store(存储) 作用于工作内存变量,将变量副本的值传送到主内存中(从工作内存读取到寄存器)。
- write(写入) 作用于主内存变量,将store传入的值写入到主内存的共享变量中(寄存器变量写入到主内存)。
注意:任意间的组合操作,不是原子的。
- 将一根变量从主内存复制到工作内存,要顺序执行read、load操作;要将变量从工作内存同步回主内存要顺序执行stroe、write操作。只要求顺序执行,不一定是连续执行(即原子操作)。
- 做了assign操作,必须同步回主内存。不能没做assign操作,同步回主内存。(不一定assign后,马上同步回主内存)
八、并发中保证变量可见性方法final synchronized volatile
九、synchronized语义规范(既可以保证可见性,又可以保证线程安全)
- 进入同步块前,先清空工作内存中的共享变量,从主内存中重新加载。
- 解锁前,必须把修改的共享变量同步回主内存。
- 最核心的,是synchronized有锁机制,只有获得锁的线程才能操作共享资源。(悲观锁)
十、 volatile语义规范(保证可见性,保证工作内存的变量和主内存的变量的值一致)
- 使用volatile变量时,必须重新从主内存加载,并且read、load是连续的。
- 修改volatile变量后,必须马上同步回主内存,并且store、write是连续的。
十一、volatile能做到线程安全吗?(不能保证原子性)
- 不能,因为它没有锁机制,线程可并发操作共享资源。
十二、volatile比synchronized,使用简单、性能稍好。
十三、volatile还可以用于限制局部代码指令重排序
- 线程A和线程B的部分代码
线程A
content = initContent();
isInit = true;
线程B
while(isinit) {
content.oper();
}
- jvm优化指令重排序后
线程A(因为两行代码,没有任何关系)
isInit = true;
content = initContent();
- 当两个线程并发执行时,就可能出现线程B中content发生空异常。
十四、volatile的使用范围
-
只可修饰成员变量(静态、非静态)。因为用来保证共享变量可见性,共享变量只能是全局变量。
-
多线程并发下,才需要使用。
网友评论