您的位置:首页 > 编程语言 > Java开发

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变量的副本

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 等
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: