关于java:Java多线程之Java内存模型

34次阅读

共计 1843 个字符,预计需要花费 5 分钟才能阅读完成。

为什么要有 Java 内存模型

Java 是运行在 Java 虚拟机的,相当于在操作系统之上建设的一个虚构的计算机,Java 虚拟机想要做到跨平台,就须要定义一种 Java 内存模型来屏蔽掉各种硬件和操作系统的内存拜访差别,通过内存模型来保障了并发场景下的一致性、原子性和有序性。

Java 内存模型的具体内容

主内存与工作内存

Java 内存模型规定了所有的变量都存储在主内存中,每个线程还有本人的工作内存,线程的工作内存中保留了该线程应用到的变量的主内存正本拷贝,线程对变量的所有操作 (读取,赋值) 都必须在工作内存中进行,而不能间接读写内存中的变量,不同线程之间也无奈间接拜访对方工作内存中的变量,线程间变量值的传递均须要通过主内存来实现。

如图,如果线程想要通信的话要执行一下步骤:
A 线程先把工作内存的值写入主内存
B 线程从主内存中去读取出 A 线程写的值

内存间交互操作
  1. lock,锁定,所用于主内存变量,它把一个变量标识为一条线程独占的状态。
  2. unlock,解锁,解锁后的变量能力被其余线程锁定。
  3. read,读取,所用于主内存变量,它把一个主内存变量的值,读取到工作内存中。
  4. load,载入,所用于工作内存变量,它把 read 读取的值,放到工作内存的变量正本中。
  5. use,应用,作用于工作内存变量,它把工作内存变量的值传递给执行引擎,当 JVM 遇到一个变量读取指令就会执行这个操作。
  6. assign,赋值,作用于工作内存变量,它把一个从执行引擎接管到的值赋值给工作内存变量。
  7. store,存储,作用域工作内存变量,它把工作内存变量值传送到主内存中。
  8. write,写入,作用于主内存变量,它把 store 从工作内存中失去的变量值写入到主内存变量中

原子性可见性有序性

Java 内存模型的相干操作和规定,是围绕着并发过程中如何解决原子性,可见性,有序性 3 个特色来建设的。

原子性
    public class TestAtomicity {
        private static int number = 0;
        public static void main(String[] args) throws InterruptedException {Runnable increment = () -> {for (int i = 0; i < 1000; i++) {number++;}
            };
            ArrayList<Thread> ts = new ArrayList<>();
            for (int i = 0; i < 5; i++) {Thread t = new Thread(increment);
                t.start();
                ts.add(t);
            }
            for (Thread t : ts) {t.join();
            }
            System.out.println("number =" + number);
        }
    }

应用 javap 反汇编 class 文件,失去上面的字节码指令:

9: getstatic #12 // Field number:I
12: iconst_1
13: iadd
14: putstatic #12 // Field number:I

由此可见 number++ 是由多条语句组成,以上多条指令在一个线程的状况下是不会出问题的,然而在多线程状况下就可能会呈现问题。比方一个线程在执行 13: iadd 时,另一个线程又执行 9: getstatic。会导致两次 number++,实际上只加了 1
论断:
并发编程时,会呈现原子性问题,当一个线程对共享变量操作到一半时,另外的线程也有可能来操作共享变量,烦扰了前一个线程的操作。

可见性
    public class TestVisibility {
        private static boolean run = true;
        public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (run) {}});
            t1.start();
            Thread.sleep(1000);
            Thread t2 = new Thread(() -> {
                run = false;
                System.out.println("工夫到,线程 2 设置为 false");
            });
            t2.start();}
    }

一个线程依据 boolean 类型的标记flag,while 循环,另一个线程扭转这个flag 变量的值,另 一个线程并不会进行循环。
论断:
并发编程时,会呈现可见性问题,当一个线程对共享变量进行了批改,另外的线程并没有立刻看到批改后的最新值

有序性

程序代码在执行过程中的先后顺序,因为 Java 在编译期以及运行期的优化,导致了代码的执行程序未必就是开发者编写代码时的程序。

正文完
 0