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++等依赖前一个数的值 等等
满足:boolean变量,记录温度变化的变量 等等2.该变量没有包含在具有其他变量的不变式中
不满足:不变式 low < up 等