0

我一直试图在一些多线程 Java 代码中引发发布错误。

下面的示例似乎应该可以解决问题,但到目前为止它按预期运行。不可否认,我在只有两个内核(和超线程)的 MacBook Pro(OSX 10.7.4 和 2.8 GHz Intel Core i7)上运行它。所以一次只能运行 4 个线程。

任何想法如何更好地引发出版失败?

package demo;

import java.util.concurrent.CyclicBarrier;

public class UnsafePublicationTest {
private static final String FIRST_VAL = "FAIL";
private static final String SECOND_VAL = "GOOD";

public void test() throws Exception {
    UnsafePublisher unsafe = new UnsafePublisher();
    unsafe.setValue(FIRST_VAL);
    CyclicBarrier gate = launchThreads(10, unsafe);
    gate.await();  // Start all threads at once
    gate.await();  // Each thread reads the first value

    // Should cause errors since update is not published
    unsafe.setValue(SECOND_VAL);
    gate.await();  // Each thread tries for the second value
    gate.await();  // Wait for the readers finish
}

private CyclicBarrier launchThreads(int count, UnsafePublisher unsafe) {
    CyclicBarrier gate = new CyclicBarrier(count + 1);
    for (int id = 0; id < count; id++) {
        ValueReader rdr = new ValueReader(id, gate, unsafe);
        rdr.start();
    }
    return gate;
}

private static class UnsafePublisher {
    private String fValue;

    public UnsafePublisher() { /* no synthetic ctor */ }

    public void setValue(String value) {
        this.fValue = value;
    }

    public String getValue() {
        return fValue;
    }
}

private static class ValueReader extends Thread {
    private final int fId;
    private final CyclicBarrier fGate;
    private final UnsafePublisher fTest;

    public ValueReader(int id, CyclicBarrier gate, UnsafePublisher test) {
        fId = id;
        fGate = gate;
        fTest = test;
    }

    public void run() {
        try {
            fGate.await();
            int noOp = this.hashCode();
            // Try to get the thread to cache the value.
            for (int i = 0; i < 10000; i ++) {
                for (int j = 0; j < 10000; j++) {
                    String first = fTest.getValue();
                    noOp = noOp ^ first.hashCode();
                    if (!FIRST_VAL.equals(first))
                        System.out.println("Thread " + fId + " read " + first);
                }
            }
            fGate.await();

            // Between these awaits, the value is changed.

            fGate.await();
            String second = fTest.getValue();
            if (!SECOND_VAL.equals(second))
                System.out.println("Thread " + fId + " read " + second);
            System.out.println("Thread " + fId + " hash " + noOp);
            fGate.await();

        } catch (Exception err) { /* ignore */ }
    }
}

public static void main(String[] args) throws Exception {
    UnsafePublicationTest test = new UnsafePublicationTest();
    test.test();
}

}

4

1 回答 1

0

这很难调试 :) 我仍在尝试了解您想要实现的目标,但是对我而言,您所看到的是正常的和预期的(如果您深入研究并理解代码)。这是发生的事情:

  • CyclicBarrier 设置为 11。

    假设主线程启动了 10 个其他线程并且它们都开始执行它们的 run 方法。但他们立即阻止。现在主线程和这 10 个线程的启动顺序在这里并不重要。这并不重要,因为在主线程中您第二次调用 gate.await(),从而允许每个线程执行 for 循环。在完成 for 循环后,他们调用 gate.await() - 第二次,从而再次解除障碍。

    • 现在是最让我困惑的部分。您在 run 方法中对 gate.await() 进行了第三次调用,因此它们都阻塞了,因为在上一步中屏障被“解除”了。现在这里的顺序又不重要了,可以说是先调用主线程,将“fValue”的值设置为“GOOD”,然后10个线程等待屏障。这并不重要。重要的是,这个声明:

代码(我们称之为sample1):

String second = test.getValue();
if (!SECOND_VAL.equals(second)){
     System.out.println("Thread " + i + " read " + second); 
}

即在 run 方法中,只有在 main 调用之后才会 100% 执行:

 unsafe.setValue(SECOND_VAL); // call it sample2

我认为问题出在某个地方(魔鬼总是在细节中:))。

如果您同意 sample2 在 sample1 之前 100% 执行,那么这段代码的几率是多少?

 if (!SECOND_VAL.equals(second)){
      System.out.println("Thread " + i + " read " + second);
 }

好吧,对我来说,没有。

因此,我的问题是,您要证明什么?可能我们可以想到一个更好/更简单的例子吗?如果您仍然想坚持这一点,那么您可能必须解释更多您想要实现的目标。

于 2012-08-23T07:34:24.610 回答