• 1. Java并发编程-Java内存模型


    并发问题的原因可以归结为三大类:可见性,有序性和原子性。

    可见性的原因是CPU缓存,不同CPU之间缓存的数据互相之间不可见。
    有序性的原因是编译优化(指令重排序)
    原子性的原因是一条高级指令可能对应多个CPU指令,而OS只能保证CPU指令的原子性。

    解决方案

    解决可见性和有序性可以通过禁用CPU缓存和禁用编译优化实现,但这样会严重影响程序性能,合理的方案应该是按需禁用。Java内存模型规范了JVM如何提供按需禁用缓存和编译优化的方法,具体包括:volatile, synchronized和final关键字以及六项Happens-Before规则

    volatile

    这个关键字不是Java语言特有的,C语言中就已引入,最原始的意义就是禁用CPU缓存。如声明一个变量 volatile int a = 1; 表示告诉编译器,对这个变量的读写禁用CPU缓存,必须从内存读写。

    Happens-Before规则

    它的含义是:前面一个操作的结果对后续操作是可见的。Happens-Before规则约束了编译器的优化行为,虽运行编译优化,但要求编译器优化后一定遵守Happens-Before规则。

    1. 程序的顺序性规则

    这条规则指:一个线程中,按照程序顺序,前面的操作happens-Before于后续的任意操作。比较容易理解,即程序前面对某个变量的修改一定是对后续操作可见的。

    2. Volatile

    对一个 volatile 变量的写操作相对于后续对这个 volatile 变量的读操作可见。

    3. 传递性

    指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。

    4. 管程中锁的规则

    指对一个锁的解锁happens-before于后续对这个锁的加锁。管程是一种通用的同步原语,在Java中就是synchronized。管程中的锁在Java中是隐式实现的,由编译器自动完成加解锁。

    即一个线程在同步代码块中对变量的写对另一个进入代码块的线程可见。

    5. 线程start()规则

    指主线程A启动子线程B后,子线程B能够看到主线程在启动B之前的操作。

    6. 线程join规则

    指主线程A等待子线程B完成(主线程A中调用B线程的join()方法实现),当B完成后,主线程能够看到子线程的操作(共享变量的值可见)。

    final关键字

    在JDK 1.5 以后 Java 内存模型对 final 类型变量的重排进行了约束。现在只要我们提供正确构造函数没有“逸出”,就不会出问题。

    小结

    Java的内存模型是并发编程领域的一次重要创新,其中happens-before规则比较难懂一些。Happens-before本质是上解决可见性的问题。如果A happens-before B, 即使A在线程1中发生,那么在线程2中也能看到A发生的事情。

  • 相关阅读:
    【PowerQuery】Excel的PowerQuery的连接组的导入与导出
    19.Feign 的工程化实例:eureka,ribbon,feign,hystrix(springcloud)
    LTR (Learning to Rank): 排序算法 poitwise, pairwise, listwise常见方案总结
    c++均值滤波:cv::blur
    给好朋友用代码画一个爱心吧
    基于51单片机电子钟闹钟12/24小时制LCD显示( proteus仿真+程序+设计报告+讲解视频)
    go slice切片的详细知识(包含底层扩容)——2
    【开源】JAVA+Vue.js实现APK检测管理系统
    java编程基础总结——30.synchronized和Lock锁解决线程安全问题
    两个难搞的Java Error/Exception
  • 原文地址:https://blog.csdn.net/qq_25027457/article/details/125596987