变量分为哪几类安全
全局变量 = 属性(静态的、非静态的) 局部变量 = 本地变量、参数bash
多线程间共享数据多线程
全局变量:静态变量或共享对象并发
并发线程能不能看到共享变量的最新值,这就是并发中变量可见性问题jvm
(1) 为何不可见?性能
(2) 怎样才能可见优化
使用synchroized关键字,对线程主体进行包装spa
使用volatile关键字修饰共享变量线程
JAVA内存模型及操做规范code
(1) 共享变量必须存放在主内存。
(2) 线程有本身的工做内存,线程只可操做本身的工做内存。
(3) 线程要操做共享变量,需从主内存中读取变量值到工做内存,改变后再从工做内存同步到主内存。
(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) 不能,由于它没有锁机制,线程可并发操做共享资源。
(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关键字的使用