什么是内存可见性

一个线程对主内存中的数据修改了,但是其他线程不可见。造成了内存可见性问题。

主内存是什么

存储共享变量

工作内存是什么

存储私有变量

  1. // 永不终止的循环 - 可见性问题
  2. public class TestInfinityLoop {
  3. static boolean stop = false; //停止标记
  4. public static void main(String[] args) throws InterruptedException {
  5. Thread t = new Thread(() -> {
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. stop = true; // volatile 的写
  12. System.out.println("t线程内存:" + stop);
  13. });
  14. System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
  15. System.out.println("主线程内存:" + stop);
  16. t.start();
  17. //Thread t2 = new Thread(() -> {
  18. // try {
  19. // Thread.sleep(1000);
  20. // } catch (InterruptedException e) {
  21. // e.printStackTrace();
  22. // }
  23. // stop = true; // volatile 的写
  24. // System.out.println("t2线程内存:" + stop);
  25. //});
  26. //t2.start();
  27. //System.out.println("主线程内存:" + stop);
  28. foo();
  29. }
  30. private static void foo() {
  31. while (true) {
  32. boolean b = stop; // volatile 的读
  33. if (b) {
  34. break;
  35. }
  36. }
  37. System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
  38. }
  39. }

start 17:30:32 主线程内存:false t线程内存:true

这个代码和视频里讲的,启动两个线程,其中stop会将主内存中值修改为TRUE,实际上,一个线程的线程结果都是将主内存中的值改为TRUE。和多线程无关。
我们来看看两个线程的结果打印

  1. // 永不终止的循环 - 可见性问题
  2. public class TestInfinityLoop {
  3. static boolean stop = false; //停止标记
  4. public static void main(String[] args) throws InterruptedException {
  5. Thread t = new Thread(() -> {
  6. try {
  7. Thread.sleep(1000);
  8. } catch (InterruptedException e) {
  9. e.printStackTrace();
  10. }
  11. stop = true; // volatile 的写
  12. System.out.println("t线程内存:" + stop);
  13. });
  14. System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
  15. System.out.println("主线程内存:" + stop);
  16. t.start();
  17. Thread t2 = new Thread(() -> {
  18. try {
  19. Thread.sleep(1000);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. stop = true; // volatile 的写
  24. System.out.println("t2线程内存:" + stop);
  25. });
  26. t2.start();
  27. System.out.println("主线程内存:" + stop);
  28. foo();
  29. }
  30. private static void foo() {
  31. while (true) {
  32. boolean b = stop; // volatile 的读
  33. if (b) {
  34. break;
  35. }
  36. }
  37. System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
  38. }
  39. }

最终输出都是

start 17:30:12 主线程内存:false 主线程内存:false t2线程内存:true t线程内存:true

  1. 初始状态, t 线程刚开始从主内存读取了 run 的值到工作内存
    内存可见性 - 图1
    再尝试用 -Xint 强制解释执行
    禁用JIT编译器优化命令
    image.png
    上面的结果

    1. start 18:10:44
    2. 主线程内存:false
    3. t线程内存:true
    4. end 18:10:45

    再用 -XX:+PrintCompilation 看看实时编译后的代码
    image.png
    这个就代表着编译被优化了。

    解决方案

    加上volatile(易变)的关键字

    1. public class TestInfinityLoop {
    2. volatile static boolean stop = false; //停止标记
    3. public static void main(String[] args) throws InterruptedException {
    4. Thread t = new Thread(() -> {
    5. try {
    6. Thread.sleep(1000);
    7. } catch (InterruptedException e) {
    8. e.printStackTrace();
    9. }
    10. stop = true; // volatile 的写
    11. System.out.println("t线程内存:" + stop);
    12. });
    13. System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    14. System.out.println("主线程内存:" + stop);
    15. t.start();
    16. foo();
    17. }
    18. private static void foo() {
    19. while (true) {
    20. boolean b = stop; // volatile 的读
    21. if (b) {
    22. break;
    23. }
    24. }
    25. System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    26. }
    27. }

    或者用synchronized解决

    1. public class TestInfinityLoop {
    2. static boolean stop = false; //停止标记
    3. private static final Object lock = new Object();
    4. public static void main(String[] args) throws InterruptedException {
    5. Thread t = new Thread(() -> {
    6. try {
    7. Thread.sleep(1000);
    8. } catch (InterruptedException e) {
    9. e.printStackTrace();
    10. }
    11. synchronized (lock) {
    12. stop = true; // volatile 的写
    13. }
    14. System.out.println("t线程内存:" + stop);
    15. });
    16. System.out.println("start " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    17. System.out.println("主线程内存:" + stop);
    18. t.start();
    19. foo();
    20. }
    21. private static void foo() {
    22. while (true) {
    23. synchronized (lock) {
    24. boolean b = stop; // volatile 的读
    25. if (b) {
    26. break;
    27. }
    28. }
    29. }
    30. System.out.println("end " + LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    31. }
    32. }

    总结

    synchronized可以解决可见性、原子性、有序性,但是它是重型锁。而volatile能解决可见性和有序性,但不能解决原子性。