volatile 关键字

x33g5p2x  于2021-12-05 转载在 其他  
字(1.6k)|赞(0)|评价(0)|浏览(526)

volatile 保证内存可见性 (作用)

内存可见性??

举例:

  1. public class ThreadDemo17 {
  2. static class Counter{
  3. public int flag = 0;
  4. }
  5. public static void main(String[] args) {
  6. Counter counter = new Counter();
  7. // 下面两个线程中涉及到的 counter 都是同一个对象
  8. Thread t1 = new Thread(){
  9. @Override
  10. public void run(){
  11. while (counter.flag == 0){
  12. }
  13. System.out.println("循环结束!!");
  14. }
  15. };
  16. t1.start();
  17. Thread t2 = new Thread(){
  18. @Override
  19. public void run(){
  20. Scanner scan = new Scanner(System.in);
  21. System.out.print("请输入一个整数: ");
  22. // 在此处修改 flag,按理说,是会影响到 t1 中的 flag
  23. counter.flag = scan.nextInt();
  24. }
  25. };
  26. t2.start();
  27. }
  28. }

预期效果:
线程1,先进入循环;线程2,会读取用户输入的一个整数
随着用户输入了一个非0 的整数后,此时,线程1 的循环就会随之终止

实际效果:

线程2,输出数据完毕后,发现线程1 的循环并没有结束
这是为什么?????

这样的现象背后,涉及到了 编译器的优化

[ 注意 ], 编译器的优化,必须保证一个前提:优化后的逻辑和优化前是等价的
但是此处,编译器错误的优化,导致程序出现了 bug ( 这是编译器自身的"坑"~~ )

上述的优化策略,就是"内存可见性"
若优化生效,内存就是不可见的了 (其他线程修改了也看不见)
若优化不生效,内存才是可见的 (其他线程修改能看见)

因此 volatile 关键字的作用,也就是:禁止编译器进行上述场景的优化
(一个线程读,一个线程写,修改对于读线程来说可能没生效)

故,刚才的代码可以加上 volatile 关键字:

  1. static class Counter{
  2. public volatile int flag = 0;
  3. }

此时的输出结果:

加上 volatile 关键字,手动禁止了这样的优化

volatile 不保证原子性

在之前的 线程安全篇 里,我们举了一个例子
那么,我们如果加上关键字 volatile,会解决这里的线程不安全问题嘛??

  1. public class ThreadDemo18 {
  2. static class Counter{
  3. // 此处加上 关键字 volatile
  4. public volatile int count = 0;
  5. public void increase(){
  6. count++;
  7. }
  8. }
  9. public static void main(String[] args) throws InterruptedException {
  10. Counter counter = new Counter();
  11. Thread t1 = new Thread(){
  12. @Override
  13. public void run(){
  14. for (int i = 0; i < 50000; i++) {
  15. counter.increase();
  16. }
  17. }
  18. };
  19. t1.start();
  20. Thread t2 = new Thread(){
  21. @Override
  22. public void run(){
  23. for (int i = 0; i < 50000; i++) {
  24. counter.increase();
  25. }
  26. }
  27. };
  28. t2.start();
  29. t1.join();
  30. t2.join();
  31. System.out.println(counter.count);
  32. }
  33. }

执行验证:

我们发现,volatile 关键字,并不能解决这个问题,最终 count 的值仍然无法保证是 100000

线程不安全的场景:
1.一个线程读,一个线程写 ( volatile 关键字解决)
2.两个线程写 (加锁解决线程安全问题)

下篇:通信 — 对象的等待集

相关文章