Java内存模型笔记

119 阅读2分钟

Happens-Before 规则

1. 程序的顺序性规则

该规则指的是在一个线程中,如果前面的操作执行Happens-Before 后面的任意操作。如下面代码,在一个线程中,int i=2操作一定发生在int j=2操作前。

int i=1;
int j=2;

2. volatile 变量规则

这条规则是指对一个 volatile 变量的写操作, Happens-Before 于后续对这个 volatile 变量的读操作。

3、传递性规则

如果操作A Happens-Before 操作B,操作B Happens-Before 操作C,那么操作A一定Happens-Before操作C。 Demo

class VolatileExample {
  int x = 0;
  volatile boolean v = false;
  public void writer() {
    x = 1;
    v = true;
  }
  public void reader() {
    if (v == true) {
      System.out.println("x="+x);
      // x=1
    }
  }
}


public class VolatileExampleMain {
    public static void main(String[] args) {
        VolatileExample volatileExample = new VolatileExample();

        Thread threadA = new Thread(() -> {
            volatileExample.writer();
        });
        
        threadA.start();
        Thread threadB = new Thread(() -> {
            volatileExample.reader();
        });
        threadB.start();

    }
}


运行结果

image.png
HappenBefore.png
根据规则一: threadA的x=1操作Hanppens-Before写变量v=true; threadB的读变量V Happens-Before读变量X; 根据规则二: 线程A的写volatile变量V Happens-Before 线程B的读volatile变量V。 根据规则三的传递性原则,线程A的写变量X一定发生在线程B的读变量X前,所以线程B的x值为1。

4、管程中锁的规则

该规则说的是一个对锁的解锁操作Happens-Before后续对该锁的加锁操作。

管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。 如下代码所示:  x 的初始值是 1,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁),线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x的值12。

public class SynchronizedExample {

    int x = 1;

    public synchronized void updateValue() {
        if (this.x < 12) {
            this.x = 12;
        }
    }

    public static void main(String[] args) {
        SynchronizedExample synchronizedExample = new SynchronizedExample();

        Thread threadA = new Thread(() -> synchronizedExample.updateValue());

        threadA.start();
        Thread threadB = new Thread(() -> synchronizedExample.updateValue());
        threadB.start();
    }
}

5、线程start规则

该规则说明的是主线程在调用子线程的start方法后,子线程能够看到主线程在启动子线程前的操作。 如下代码,data的初始值为1,调用threadB.start()方法前,data的值为77。根据该规则,子线程threadB能够看到data的值为77。

public class ThreadExample {

    public static void main(String[] args) {
        final Data data = new Data();
        data.setValue(1);
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(data.getValue());
            }
        });
        data.setValue(77);
        threadB.start();
    }

    static class Data {
        private int value;

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }
}

6、线程 join() 规则

这条是关于线程等待的。它是指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。

public class JoinExample {

    public static void main(String[] args) throws InterruptedException {
        final Data data = new Data();
        data.setValue(1);
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                data.setValue(77);
            }
        });

        threadB.start();
        threadB.join();
        System.out.println("Main Thread data.value="+data.getValue());
    }

    static class Data {
        private int value;

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }
}