可见性问题

线程2会结束运行吗?

  1. package com.qf;
  2. import java.util.concurrent.TimeUnit;
  3. public class VolatieDemo {
  4. private boolean flag = false;
  5. public void setFlag(boolean flag) {
  6. this.flag = flag;
  7. }
  8. public boolean getFlag() {
  9. return flag;
  10. }
  11. public static void main(String[] args) throws InterruptedException {
  12. final VolatieDemo demo = new VolatieDemo();
  13. final Thread t1 = new Thread(new Runnable() {
  14. @Override
  15. public void run() {
  16. demo.setFlag(true);
  17. System.out.println(demo.getFlag());
  18. }
  19. });
  20. final Thread t2 = new Thread(new Runnable() {
  21. @Override
  22. public void run() {
  23. while (!demo.flag){
  24. }
  25. System.out.println("t2 is done");
  26. }
  27. });
  28. t2.start();
  29. TimeUnit.SECONDS.sleep(1);
  30. t1.start();
  31. }
  32. }

JMM (Java Memory Model)

问题起源:CPU和缓存一致性

image.png

动态演示缓存一致性协议 MESI

https://www.scss.tcd.ie/Jeremy.Jones/VivioJS/caches/MESIHelp.htm

什么是内存模型

内存模型定义了共享内存系统中多线程程序读写操作行为的规范。

什么是Java内存模型

  • Java内存模型将内存划分为主内存和工作内存。
  • 所有的变量都存储在主内存中,每个线程有自己的工作内存。
  • 线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝。
  • 线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。
  • 不同的线程之间也无法直接访问对方工作内存中的变量。
  • 线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

image.png

JMM八种原子操作

  1. lock 锁定 : 把主内存中的一个变量标志为一个线程独享的状态
  2. unlock 解锁 : 把主内存中的一个变量释放出来
  3. read 读:将主内存中的变量读到工作内存中
  4. load 加载:将工作内存中的变量加载到副本中
  5. use 使用:当执行引擎需要使用到一个变量时,将工作内存中的变量的值传递给执行引擎
  6. assign 赋值:将执行引擎收的的值赋值给工作内存中的变量
  7. store 存储:将工作内存中的变量的值传到主内存中
  8. write 写入:将store得到值放到主内存的变量中

    Java内存模型原语

    Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。例如,volatile和synchronize关键字。

    Volatile工作原理分析

    volatile如何解决内存可见性的?
    image.png

Volatile无法解决原子性问题

案例

  1. package com.qf;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. public class VolatileAtomicDemo {
  5. public volatile static int count = 0;
  6. public static void main(String [] args) throws InterruptedException {
  7. List<Thread> list = new ArrayList<>();
  8. //开启10个线程
  9. for(int i = 0;i < 10; i++){
  10. list.add( new Thread(new Runnable() {
  11. @Override
  12. public void run() {
  13. try {
  14. Thread.sleep(1000);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. //每个线程中让count的值自增100次
  19. for(int j = 0;j < 1000;j++){
  20. count++;
  21. }
  22. }
  23. }));
  24. }
  25. for (Thread thread : list) {
  26. thread.start();
  27. }
  28. for (Thread thread : list) {
  29. thread.join();
  30. }
  31. System.out.println("count= " + count);
  32. }
  33. }

思考:问题出在哪里?