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
















