java8中使用volatile实现Acquire/Release模型
就我而言,我想Acquire/Release在 java8 中使用volatile.
所以我写的代码使用了一个 volatile 共享变量I来保证MAP其他线程可以看到的修改。
public static volatile int I = 0;
public static final Map<String, String> MAP = new HashMap<>();
// run at Thread-1
public static void write(){
MAP.put("test", "test");
I++; // release
}
// run at Thead-2
public static void read(){
int i = I; // acquire
MAP.get("test"); // want to see the modifying by write()
}
我的问题是:
- 代码同步是否正确?
- 有没有可能是 JIT 消除了未使用的局部变量,
i从而导致acquire操作无效?
回答
首先要注意++的一个volatile变量是不是原子,因此,你不能在多个更新的情况下,依靠它的价值。
只要只有一个更新,检查更新是否确实发生就足够了,但执行检查至关重要。否则,我们无法保证的(应该是采集)挥发性读取被后续的(应该是释放)volatile更新。
只需考虑以下时间:
Thread 1 Thread 2
? ? [ Read I ]
? map update ? ? ?
? ? ? map query ?
[ Write I ] ? ?
在这里,两个线程同时使用地图,这是无可救药的坏了,而获取和释放动作没有结果,因为获取是不是以后的版本。
如果您检查已读取的值并仅在它是另一个线程写入的预期值时继续,则您只能依赖这种关系。
由于整个构造仅适用于单个更新,因此您可以使用 aboolean代替:
private static volatile boolean I = false;
private static final Map<String, String> MAP = new HashMap<>();
// run at Thread-1
public static void write(){
MAP.put("test", "test");
I = true; // release
}
// run at Thead-2
public static void read(){
if(I) { // acquire
MAP.get("test"); // want to see the modifying by write()
}
}
您不能将它用于多次更新,因为想要执行第二次更新的线程必须确保在第一次更新后读取地图的所有线程完成之前不开始更新地图。但是使用这种方法根本无法获得此信息。