一、什么是多线程下共享变量的可见性问题?
在多线程环境下,如果一个线程(比如线程A)修改了某个共享变量的值,但是其他线程(比如线程B)无法立即感知到这个变化,这种情况通常被称为“可见性问题”。
可见性问题是多线程编程中常见的一种并发问题,其主要原因包括以下几点:
java的内存模型:在多线程环境下,每个线程都有自己的工作内存和高速缓存,线程对共享变量的操作通常是在工作内存中进行的。当一个线程修改了共享变量的值时,这个变化可能会延迟到主内存中,而其他线程可能仍然在使用自己的工作内存中缓存的旧值,导致对该变量的修改对其他线程不可见。
指令重排序:为了提高性能,编译器和处理器可能会对指令进行重排序优化,这可能会导致操作的执行顺序与代码中的顺序不同。如果一个线程在写入共享变量后立即执行了一个写入操作,而这个写入操作又被重排序到写入操作之前执行,那么其他线程可能会看到这个写入操作之前的值,而不是最新的值。
缓存一致性协议:现代计算机系统通常采用缓存一致性协议来维护多个处理器或核之间的内存一致性。这些协议可能导致在一个处理器对共享变量进行写入后,其他处理器的缓存中的相应数据可能不会立即被更新,而是在一定时间后才会被刷新,这会导致其他线程对共享变量的值不可见。
二、什么是Happens-Before
Happens-Before 是一种可见性模型,JMM 通过 Happens-Before 关系向开发人员提供跨越线程的内存可见性保证。如果一个操作的执行结果对另外一个操作可见,那么这两个操作之间必然存在Happens-Before 管理。
三、常见的 Happens-Before 规则。
-
程序顺序规则
一个线程中的每个操作,happens-before 这个线程中的任意后续操作。也就是不管怎么重排序,单线程的程序的执行结果不能改变 -
传递性规则
也就是 A Happens-Before B,B Happens-Before C。就可以推导出 A Happens-Before C -
volatile 变量规则
对一个 volatile 修饰的变量的写一定 happens-before 于任意后续对这个 volatile 变量的读操作 -
监视器锁规则
在这个场景中,如果线程 A 获得了锁并且把 x 修改成了 12,那么后续的线程获得锁之后得到的 x 的值一定是 12 -
线程启动规则
如果线程 A 执行操作 ThreadB.start(),那么线程 A 的ThreadB.start()之前的操作 happens-before 线程 B 中的任意操作 -
join 规则
如果线程 A 执行操作 ThreadB.join()并成功返回,那么线程 B 中的任意操作 happens-before 于线程 A 从 ThreadB.join()操作成功的返回。