我知道这意味着如果你声明一个数组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;
}
}
}
在我的第一个滤镜算法实现中,我错过volatile
变量level
和lastToEnter
,毫不奇怪,程序进入无限循环。添加缺少的volatile
后,程序可以按预期结束。
正如我在开始时所说的那样,volatile
数组并不意味着数组中的项是易变的,那么为什么程序在添加缺失的volatile
之后可以按预期结束?
当我实施另一个互联网算法时,我问自己这个问题,在我添加volatile
关键字后,该算法仍然无法正常运行。我必须使用一个技巧(Java volatile array?)来使数组中的项看起来像volatile一样:(下面的代码可以直接粘贴到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;
}
答案 0 :(得分:1)
Java中的易失性数组不包含易失性元素 - 但如果您通过数组引用(易失性)访问它们,您将获得易失性读取。例如,在上面的代码中:
static volatile int lastToEnter[] = new int[N - 1];
是易失性写入,而
lastToEnter[i] = this.id;
不是。但是,评估数组值 - 例如:
lastToEnter[i] == this.id
是易失性读取 - 您首先读取对易失性数组的引用,然后才访问第i个元素以评估其值。
我怀疑这是将数组声明为volatile后执行成功的原因。