如何在Java中引发并发发布错误

时间:2012-08-22 23:39:11

标签: java multithreading concurrency

我一直试图在某些多线程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();
}

}

1 个答案:

答案 0 :(得分:0)

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

  • CyclicBarrier设置为11.

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

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

代码(我们称之为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);
 }

好吧,对我来说,没有。

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