并发编程 - Happens-before

    技术2024-07-23  75

    一、Happens-before

    Happens-before: 前面的操作结果对后续操作是可见的。 要求编译器的优化必须遵许Happens-before原则

    二、Happens-before 六大原则

    2.1 程序的顺序性原则

    单线程中,某一个变量的修改对后续的操作一定是可见的。 如下所示的程序中,第1句和第2句的顺序编译器可以调换(不影响程序结果),但是1和2必须要在3之前执行完成才可以哦(3有volatile关键字)。

    public class Test { volatile static int z; public static void main(String[] args) { int x = 1; // 1 int y = 2; // 2 z = 3; // 3 } }

    2.2 volatile 变量规则

    volatile 变量的写操作,要Happens-before,volatile 变量的读操作。 假设有两个线程同时进入到了write和read方法,那么,falg = true(写的操作)是先于 if(falg) (读的操作),不会因为cpu缓存导致写了读不到,一旦有写入,必定可以读到。

    public class Test { static int x = 0; volatile static boolean falg = false; private static void write(){ x = 1; falg = true; } private static void read() { if(falg) { System.out.println(x); } } }

    2.3 传递性

    A happens - before B,且B happens - before C,那么A一定 happens-before C; 还是上一个例子。 已知:x=1 优先于 falg = true (规则2.1) falg = true 优先于 if(flag) (规则2.2) 那么读到的x的值一定是1(规则2.3)

    2.4 管程中锁的规则

    管程是同步的原语,java中就指的是synchronized。 如下例子:线程A进入了这段同步块,加锁,将x的值改为2,然后解锁。 线程B进入这段同步块,加锁,读的x的值一定是2。 即:对于同一把锁,解锁操作一定先于加锁操作。

    public class Test { int x = 0; private void test() { synchronized (this) { if(this.x < 1) { this.x = 2; } } } }

    2.5 线程的start规则

    主线程调用了子线程的start操作,那子线程是可以看到主线程的之前的所有操作的。

    如下例子中,主线程对var的修改对于子线程b是可见的。

    public class Test { static int var = 1; public static void main(String[] args) { Thread B = new Thread(()->{ System.out.println(var); // 99 }); var = 99; B.start(); } }

    2.6 线程的join()规则

    主线程等待子线程完成(调用子线程的join()方法),当子线程完成后,主线程是可以看到子线程关于共享变量的修改的。 如下: 主线程中启用了子线程b,子线程b修改了var的值,执行了join之后,主线程是可以看到子线程修改后的值的。

    public class Test { static int var = 1; public static void main(String[] args) throws InterruptedException { Thread B = new Thread(()->{ var = 99; }); B.start(); B.join(); System.out.println(var); //99 } }
    Processed: 0.017, SQL: 9