happens-before规定了对共享变量的写操作对其它线程的读操作可见,它是可见性与有序性的一套规则总结,抛开以下happens-before规则,JMM并不能保证一个线程对共享变量的写,其它线程对该共享变量的读可见

  • 线程对共享变量访问时,如果将sychronized加在共享变量之外,那么能够保证变量的可见性 ```java public class Test {

    static int x; static Object m = new Object();

  1. public static void main(String[] args) {
  2. new Thread(()->{
  3. synchronized (m){
  4. x=10;
  5. }
  6. },"t1").start();
  7. new Thread(()->{
  8. synchronized(m){
  9. System.out.println(x);
  10. }
  11. },"t2").start();
  12. }

}

  1. 对共享变量的访问如 x=10; System.out.println(x) 套在synchronized之中,可以保证对变量x访问的可见性。
  2. - 线程对 volatile 变量的写,对接下来其它线程对该变量的读可见
  3. ```java
  4. public class Test {
  5. volatile static int x;
  6. public static void main(String[] args) {
  7. new Thread(()->{
  8. x=10;
  9. },"t1").start();
  10. new Thread(()->{
  11. System.out.println(x);
  12. },"t2").start();
  13. }
  14. }
  • 线程 start 前对变量的写,该线程开始后对该变量的读可见 ```java public class Test {

    volatile static int x;

    public static void main(String[] args) {

    1. x=10;
    2. new Thread(()->{
    3. System.out.println(x);
    4. },"t2").start();

    } }

  1. - 线程结束前对变量的写,对其它线程得知它结束后的读可见(比如其它线程调用t1.Alive() t1.join()等待它结束)
  2. ```java
  3. public class Test {
  4. volatile static int x;
  5. public static void main(String[] args) throws InterruptedException {
  6. Thread t1 = new Thread(()->{
  7. x=10;
  8. },"t1");
  9. t1.start();
  10. t1.join();
  11. System.out.println(x);
  12. }
  13. }

线程已经结束,那么x=10一定是写回到了主存中。

  • 对变量默认值(0,false,null)的写,对其它线程对该变量的读可见
  • 具有传递性,配合volatile的防重排指令,有如下例子 ```java public class Test {

    volatile static int x; static int y;

    public static void main(String[] args) throws InterruptedException {

    1. Thread t1 = new Thread(()->{
    2. y=10;
    3. x=20;
    4. },"t1");
    5. t1.start();
    6. new Thread(()->{
    7. System.out.println(x);
    8. System.out.println(y);
    9. },"t2").start();

    } }

``` volatile 修饰x变量,x=20相当于写,会加入写屏障,写屏障前的所有赋值均会写进主内存中,重要的是,当x被volatile修饰后,线程t2再读x值,一定是从主存中读取而不是从缓存/工作内存中读取。

注意:

除了synchronized修饰、volatile修饰的两种情况,接下来的几种情况均是因为System.out.println()方法本身保证了从主内存中读取值。
所以除了synchronized修饰、volatile修饰能够保证从主内存中读值,其它情况应该是错误的,因为读值还是可能从cache中读取,要擦亮双眼。