java - volatile 数组的奇怪行为

标签 java concurrency mutex volatile

我知道这意味着如果您声明一个数组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;
        }
    }
}

在我第一次实现过滤算法时,我错过了变量levellastToEntervolatile,毫不奇怪,程序进入了无限环形。添加缺少的 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/

相关文章:

java - 如何多次将事件从组件发送到其父组件?

Java 和 SSL 证书

java - 从 TreeMap [keySet()] 获取键 vector

c++ - 单线程可以双重锁定互斥量吗?

multithreading - 通过设置亲和性在单核上运行多线程程序的性能?

c++ - 在这种情况下,我应该使用 lock_guard、scoped_lock 还是 unique_lock?

java - 传播异常以丰富路由

java - 非最终字段的安全初始化

java - java中多线程的并发

java - 即使正在释放许可证,Semaphore 类也会陷入僵局吗?