【问题标题】:How to provoke a concurrent publishing error in Java如何在 Java 中引发并发发布错误
【发布时间】:2012-08-22 23:39:10
【问题描述】:

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

}

【问题讨论】:

    标签: java multithreading concurrency


    【解决方案1】:

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

    • 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);
     }
    

    嗯,对我来说,没有。

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

    【讨论】:

    • 测试的重点是“sample2”代码更改了 unsafe 中保存的值而没有正确发布它(没有锁定任何对象)。如果其他十个线程中的任何一个已经缓存了该值,它应该在“sample3”中报告虚假值。
    • @nyc10003 您正在使用 CyclicBarrier - 它保证正确更新对象。这可能会有所帮助:stackoverflow.com/questions/7493140/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2012-06-07
    • 1970-01-01
    • 2019-09-16
    • 2016-04-28
    相关资源
    最近更新 更多