目录
1.volatile 能保证内存可见性
volatile修饰的变量,能保证“内存可见性”。
代码在写入volatile修饰的变量的时候,
- 改变线程工作内存中volatile变量的值
- 将改变的副本的值从工作内存刷新到主内存
代码在读取volatile修饰的变量的时候,
- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取volatile变量的副本
代码示例
在这个代码中
- 创建两个线程t1和t2
- t1中包含一个循环,这个循环以flag==0为循环条件
- t2中从键盘读入一个整数,并把这个整数赋值给flag
- 预期当用户输入非0的值的时候,t1线程结束
static class Counter {
public int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
while (counter.flag == 0) {
// do nothing
}
System.out.println("循环结束!");
});
Thread t2 = new Thread(() -> {
Scanner scanner = new Scanner(System.in);
System.out.println("输⼊⼀个整数:");
counter.flag = scanner.nextInt();
});
t1.start();
t2.start();
}
// 执⾏效果
// 当⽤⼾输⼊⾮0值时,t1线程循环下不会结束,(这显然是一个bug)
t1读的是自己工作内存中的内容
当t2对flag变量进行修改,此时t1感知不到flag的变化
如果给flag加上volatile
static class Counter {
public volatile int flag = 0;
}
// 执⾏效果
// 当⽤⼾输⼊⾮0值时, t1 线程循环能够⽴即结束.
2.volatile不保证原子性
volatile和synchronized有着本质的区别。synchronized 能够保证原子性,volatile 保证的是内存可见性。
代码示例
这个是最初的演示线程安全的代码
- 给increase 方法去掉synchronized
- 给 count 加上 volatile 关键字。
static class Counter {
volatile public int count = 0;
void increase() {
count++;
}
}
public static void main(String[] args) throws InterruptedException {
final Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
counter.increase();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 50000; i++) {
counter.increase();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}
此时可以看到,最终count的值仍然无法保证是100000.