Java 并发-内存模型
本文介绍 Java 并发编程中的内存模型,volatile 关键字的使用,Happens-Before 规则,以及 final 关键字对重排序的影响

# Java 并发-内存模型

# Java 内存模型

可见性、原子性、有序性问题是并发编程中的 BUG 源头,是编程领域的共性问题,可见性和原子性的产生原因是缓存和指令重排,Java 语言提供了内存模型来规避此类问题

Java 内存模型是一个复杂的规范,基本的原理就是禁用缓存和指令重排,提供给开发者的手段就是volatilesynchronizedfinal 三个关键字,以及六项Happens-Before规则

# volatile 关键字

  1. volatile 关键字在许多语言中都有用到,原始含义就是禁用 CPU 缓存

  2. volatile 解决可见性问题

    public class App {
        volatile static boolean flag = true;
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                while (flag) {
                    // 
                }
                System.out.println("end");
            }).start();
            TimeUnit.SECONDS.sleep(5);
            flag = false;
        }
    }
    

    主线程修改 flag,子线程内的死循环是否会结束?

    如果不使用 volatile 声明,子线程一直会读取缓存内的值,而看不见其他线程的修改

    使用 volatile 关键字,强制线程通过内存对该变量进行读写,而不是 CPU 缓存

  3. volatile 解决有序性问题

    在 JDK1.5 之前,volatile 被设计用来禁用缓存,但是并不关心指令的排序问题

    在 JDK1.5 对 volatile 进行了增强,具体内容就是增加了一项 Happens-Before 规则

# Happens-Before 规则

Happens-Before 规则是 Java 内存模型规范的重要部分

规则要表达的核心内容是在特定场景下,前面一个操作的结果对后续操作是可见的

  1. 程序的顺序性规则

    在单线程中对一个变量的操作对后续的操作是可见的

  2. volatile 变量规则

    对一个 volatile 变量的写操作对后续的读操作是可见的(多线程的顺序性规则)

  3. 传递性规则

    即 A 操作对 B 操作可见,B 操作对 操作可见,那么 A 操作对 C 操作可见

  4. 管程中锁的规则

    一个线程的解锁操作对下一个线程加锁操作是可见的,如使用 synchronized

  5. 线程 start 规则

    主线程调用子线程的 start 方法对子线程中的操作是可见的

  6. 线程 join 规则

    子线程中的操作对主线程 join 方法是可见的

# final 关键字

  1. 重排序可能导致一个线程看到一个对象的时候,这个对象还没有初始化完毕

    部分初始化或者完全没有经过初始化,因为普通变量的写入是可以被重排序到构造器之外的

  2. Java 内存模型要求编译器对 final 关键字特殊处理

    即禁止处理器将 final 变量初始化操作重排序到构造器之外

Comment here, be cool~

Copyright © 2020 CadeCode

Theme 2zh powered by VuePress

本页访问次数 0

Loading