Java多线程之内存可见性——volatile
2017-01-02 17:47
281 查看
volatile关键字:
1.能够保证volatile变量的可见性
2.不能保证volatile变量的复合操作的原子性
volatile是通过加入内存屏障和禁止重排序优化来实现内存可见性。
java中具体的屏障指令就不说了,可以自行搜索
通俗来说:
volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,
而当该变量发生变化时,又会强迫线程将线程将最新的值刷新到主内存。
这样任何时刻,不同的线程总能看到该变量的最新值。
那么volatile的读写操作的过程:
线程写volatile变量的过程:
1.改变线程工作内存中volatile变量的副本的值
2.将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
1.从主内存中读取volatile变量的最新值到线程的工作内存中
2.从工作内存中读取volatile变量的副本
解决方案即为保证变量的原子性即可:
1.使用synchronized关键字
2.使用ReentrantLock(java.util.concurrent.locks包下)
3.使用AtomicInteger(vava.util.concurrent.atomic包下)
解决代码:
volatile使用的场合 :
1.对变量的写入操作不依赖其当前值
不满足:number++等依赖前一个数的值 等等
满足:boolea
b148
n变量,记录温度变化的变量 等等
2.该变量没有包含在具有其他变量的不变式中
不满足:不变式 low < up 等
1.能够保证volatile变量的可见性
2.不能保证volatile变量的复合操作的原子性
volatile是通过加入内存屏障和禁止重排序优化来实现内存可见性。
java中具体的屏障指令就不说了,可以自行搜索
通俗来说:
volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,
而当该变量发生变化时,又会强迫线程将线程将最新的值刷新到主内存。
这样任何时刻,不同的线程总能看到该变量的最新值。
那么volatile的读写操作的过程:
线程写volatile变量的过程:
1.改变线程工作内存中volatile变量的副本的值
2.将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
1.从主内存中读取volatile变量的最新值到线程的工作内存中
2.从工作内存中读取volatile变量的副本
public class Demo { //由于volatile不具备原子性(同步),所以代码运行中出现小于500的情况 private volatile int number = 0; public int getNumber() { return this.number; } public void increase() { try { Thread.sleep(100); //休眠是为了使实验结果更明显 } catch(InterruptedException e) { e.printStackTrace(); } this.number++; } public static void main(String args[]) { final Demo demo = new Demo(); for(int i=0;i<500;i++) { new Thread(new Runnable() { //覆写run方法 public void run() { Demo.increase(); } }).start(); } //如果子线程都运行完了,主线程再继续往下执行 while(Thread.activeCount() > 1) { //判断是否500个子线程是否执行完 Thread.yield(); } System.out.println("number:"+Demo.getNumber()); } } /* 简要分析: 当number为100时, 1.线程A读取number的值 2.线程B读取number的值 3.线程B执行加1操作 4.线程B写入最新的number的值 5.线程A执行加1操作 6.线程A写入最新的number的值 不难发现,两次number++只增加了1,这就是原因所在 */
解决方案即为保证变量的原子性即可:
1.使用synchronized关键字
2.使用ReentrantLock(java.util.concurrent.locks包下)
3.使用AtomicInteger(vava.util.concurrent.atomic包下)
解决代码:
//第一种方案,在numbe++加synchronized关键字 public void increase() { try { Thread.sleep(100); } catch(InterruptedException e) { e.printStackTrace(); } synchronized(this) { //缩小锁粒度,没必要锁整个方法 this.number++; } }
//第二种方案,使用lock锁 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Demo { //锁操作还是很强的,感兴趣可以深入了解下 private Lock lock = new ReentrantLock; private int number = 0; public int getNumber() { return this.number; } public void increase() { try { Thread.sleep(100); } catch(InterruptedException e) { e.printStackTrace(); } //这里的加锁解锁,相当于进入退出synchronized代码块 //同时也可以保证number的可见性,number++的原子性 lock.lock(); try { //之所以加入try语句,是因为锁内部的操作可能会抛出异常 this.number++; } finally { lock.unlock(); } } //mian方法不变 }
volatile使用的场合 :
1.对变量的写入操作不依赖其当前值
不满足:number++等依赖前一个数的值 等等
满足:boolea
b148
n变量,记录温度变化的变量 等等
2.该变量没有包含在具有其他变量的不变式中
不满足:不变式 low < up 等
相关文章推荐
- java多线程 -- volatile 关键字 内存 可见性
- Java之多线程内存可见性_2(volatile不能保证原子性)
- Java之多线程内存可见性_3(synchronized和volatile比较)
- Java多线程之内存可见性——synchronized与volatile比较
- java多线程之内存可见性-synchronized、volatile
- java多线程之内存可见性-synchronized、volatile
- Java之多线程内存可见性_2(volatile实现可见性)
- Java之多线程内存可见性_2(synchronized实现可见性代码)
- Java多线程之内存可见性
- 【java多线程系列】java中的volatile的内存语义
- Java 线程概述: 线程种类、状态,原子性、内存可见性、synchronized、volatile
- java多线程内存可见性1
- volatile关键字作用与内存可见性、指令重排序概述[JAVA]
- Java多线程之内存可见性
- Java并发:volatile内存可见性和指令重排
- 【java多线程系列】java中的volatile的内存语义
- Java并发:volatile内存可见性和指令重排
- Java之多线程内存可见性_1
- Java多线程中的内存可见性与原子性分析
- Java多线程之内存可见性