jmm-内存屏障
在Java内存模型(JMM)中,内存屏障(Memory Barrier)是一种同步机制,用于确保特定的内存操作顺序,并保证内存可见性。内存屏障通过阻止处理器和编译器对内存操作进行重排序,从而避免多线程编程中可能出现的内存可见性问题。
整体介绍
Java规范定义了几种类型的内存屏障,主要分为以下几类:
- LoadLoad屏障:在
Load1; LoadLoad; Load2
这样的序列中,确保Load1
数据的装载先于Load2
及所有后续装载指令的装载。这意味着在LoadLoad
屏障之前的读操作必须在LoadLoad
屏障之后的读操作之前完成。 - StoreStore屏障:在
Store1; StoreStore; Store2
这样的序列中,确保Store1
的数据对其他处理器可见(刷新到主内存),先于Store2
及所有后续存储指令的存储。即StoreStore
屏障之前的写操作必须在StoreStore
屏障之后的写操作之前完成并对其他处理器可见。 - LoadStore屏障:在
Load1; LoadStore; Store2
这样的序列中,确保Load1
数据的装载先于Store2
及所有后续存储指令的存储。也就是LoadStore
屏障之前的读操作必须在LoadStore
屏障之后的写操作之前完成。 - StoreLoad屏障:在
Store1; StoreLoad; Load2
这样的序列中,确保Store1
的数据对其他处理器可见(刷新到主内存),先于Load2
及所有后续装载指令的装载。这是最强大的内存屏障,因为它同时包含了写操作的完成和读操作的开始顺序保障,且会使处理器的高速缓存失效,强制从主内存读取数据。
表格对照
内存屏障类型 | 指令序列示例 | 作用 |
---|---|---|
LoadLoad屏障 | Load1; LoadLoad; Load2 | 保证 Load1 先于 Load2 及后续所有读操作完成 |
StoreStore屏障 | Store1; StoreStore; Store2 | 保证 Store1 对其他处理器可见先于 Store2 及后续所有写操作 |
LoadStore屏障 | Load1; LoadStore; Store2 | 保证 Load1 先于 Store2 及后续所有写操作完成 |
StoreLoad屏障 | Store1; StoreLoad; Load2 | 保证 Store1 对其他处理器可见先于 Load2 及后续所有读操作,且强制从主内存读取数据 |
解析
- LoadLoad屏障:
- 目的:防止处理器将
LoadLoad
屏障之后的读操作重排序到屏障之前,确保读操作的顺序性。 - 示例:假设在多线程环境下,一个线程先读取共享变量
a
,然后通过LoadLoad
屏障,再读取共享变量b
。如果没有LoadLoad
屏障,处理器可能会尝试将对b
的读取操作提前执行,但有了LoadLoad
屏障,就保证了先读取a
,再读取b
。
- 目的:防止处理器将
- StoreStore屏障:
- 目的:防止处理器将
StoreStore
屏障之后的写操作重排序到屏障之前,确保写操作对其他处理器的可见性顺序。 - 示例:在一个线程中,先对共享变量
x
进行赋值,然后通过StoreStore
屏障,再对共享变量y
进行赋值。StoreStore
屏障保证了对x
的赋值先对其他处理器可见,然后才是对y
的赋值。
- 目的:防止处理器将
- LoadStore屏障:
- 目的:防止处理器将
LoadStore
屏障之后的写操作重排序到屏障之前的读操作之前,确保读操作先于写操作完成。 - 示例:当一个线程先读取共享变量
m
,然后通过LoadStore
屏障,再对共享变量n
进行赋值时,LoadStore
屏障保证了读取m
的操作完成后,才会进行对n
的赋值操作。
- 目的:防止处理器将
- StoreLoad屏障:
- 目的:这是最强大的内存屏障,它不仅防止了写操作和读操作的重排序,还保证了写操作完成后,后续的读操作能获取到最新的数据。因为它会使处理器的高速缓存失效,强制从主内存读取数据。
- 示例:在一个线程中,先对共享变量
p
进行赋值,然后通过StoreLoad
屏障,再读取共享变量q
。StoreLoad
屏障保证了对p
的赋值操作完成并对其他处理器可见后,才会读取q
,并且读取q
时能获取到最新的值。
在Java中,volatile
关键字的实现就利用了内存屏障。对 volatile
变量的读操作相当于有一个 LoadLoad
和 LoadStore
屏障,而写操作相当于有一个 StoreStore
和 StoreLoad
屏障,从而保证了 volatile
变量的可见性和禁止指令重排序的特性。同样,synchronized
关键字在进入和退出同步块时也会有类似内存屏障的效果,以确保同步块内操作的内存可见性和顺序性。