我知道这意味着如果您声明一个数组volatile
,那么对数组的引用是易变的,而不是数组中的项目。
我正在学习互斥算法,所以我写了一些测试代码:
public class MutualExclusion {
static final int N = 10;
static final int M = 100000;
volatile static int count = 0;
public static void main(String[] args) {
Thread[] threads = new Thread[N];
for (int i = 0; i < N; i++) {
Thread t = new Worker(i);
threads[i] = t;
t.start();
}
for (Thread t: threads) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count != N * M) {
System.out.println("count (" + count + ") != N * M (" + String.valueOf(N * M) + ")");
}
}
static class Worker extends Thread {
int id;
Worker(int id) {
this.id = id;
}
@Override
public void run() {
for (int i = 0; i < M; i++) {
this.lock();
// critical section
count++;
if (i % 1000 == 0) {
System.out.println(this.getName() + ": " + count);
}
this.unlock();
}
}
void lock() {
filterLock();
}
void unlock() {
filterUnlock();
}
static volatile int level[] = new int[N];
static volatile int lastToEnter[] = new int[N - 1];
void filterLock() {
for (int i = 0; i < (N - 1); i++) {
level[this.id] = i;
lastToEnter[i] = this.id;
outer:
while (lastToEnter[i] == this.id) {
for (int k = 0; k < N; k++ ) {
if (k != this.id && level[k] >= i) {
continue outer;
}
}
break;
}
}
}
void filterUnlock() {
level[this.id] = -1;
}
}
}
在我第一次实现过滤算法时,我错过了变量level
和lastToEnter
的volatile
,毫不奇怪,程序进入了无限环形。添加缺少的 volatile
后,程序可以按预期结束。
我一开始就说了,volatile
数组并不代表数组中的项是volatile的,那为什么我添加了缺失的volatile
之后程序还能正常结束呢? >?
我在实现另一个互斥算法时问过自己这个问题,在我添加 volatile
关键字后仍然无法正确运行。我必须使用技巧 ( Java volatile array? ) 使数组中的项目看起来像易变的:(下面的代码可以直接粘贴到 Worker
类中)
volatile static boolean[] b = new boolean[N];
volatile static boolean[] c = new boolean[N];
volatile static int k = 0;
void dijkstraLock() {
b[this.id] = false;
outer:
for (;;) {
if (k == this.id) {
c[this.id] = false;
c = c; // IMPORTANT! the trick
for (int i = 0; i < N; i++) {
if (i != this.id && !c[i]) {
continue outer;
}
}
break;
} else {
c[this.id] = true;
if (b[k]) {
k = this.id;
}
}
}
}
void dijkstraUnlock() {
b[this.id] = true;
c[this.id] = true;
}
最佳答案
Java 中的 volatile 数组不包含 volatile 元素 - 但如果您通过数组引用(它是 volatile)访问它们,您将获得 volatile 读取。例如,在上面的代码中:
static volatile int lastToEnter[] = new int[N - 1];
是 volatile 写入,而
lastToEnter[i] = this.id;
不是。然而,数组值的评估——例如:
lastToEnter[i] == this.id
是volatile read - 您首先读取对可变数组的引用,然后才访问第 i 个元素以评估其值。
我怀疑这就是数组被声明为 volatile 后执行成功的原因。
关于java - volatile 数组的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44350104/