jvm重新排序/能见度效应测试

时间:2011-06-07 11:24:04

标签: java multithreading concurrency memory-visibility

在编写一些java文章时,我试图在多线程环境中出现不同步的对象构造时重现re-ordering。 构建重型对象时没有同步/挥发性/韵母和其他线程在构造函数调用后立即访问它的情况。 这是我尝试的代码:

public class ReorderingTest {
    static SomeObject<JPanel>[] sharedArray = new SomeObject[100];

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            String name = "watcher" + i;
            new Thread(new Watcher(name)).start();
            System.out.printf("watcher %s started!%n", name);
        }
    }

    static class Watcher implements Runnable {
        private String name;

        Watcher(String name) {
            this.name = name;
        }

        public void run() {
            while (true) {
                int randomIndex = (int) (Math.random() * sharedArray.length);
                SomeObject<JPanel> item = sharedArray[randomIndex];
                if (item == null) {
                    //System.out.printf("sharedArray[%s]=null%n", randomIndex);
                    double r = 1 + Math.random() * 1000;
                    sharedArray[randomIndex] = new SomeObject<JPanel>(
                            new JPanel(), UUID.randomUUID().toString(), r, (float)r * 33, (long)r);
                } else {
                    //System.out.printf("sharedArray[%s]=<obj>!%n", randomIndex);
                    if (item.value == null ||
                            (item.stringField == null) ||
                            (item.doubleField == 0) ||
                            (item.floatField == 0) ||
                            (item.longField == 0)
                            ) {
                        System.err.printf("watcher %s sees default values: %s!%n", name, item);
                    } else {
                        // fully initialized! run new construction process
                        double r = 1 + Math.random() * 1000;
                        sharedArray[randomIndex] = new SomeObject<JPanel>(
                                new JPanel(), UUID.randomUUID().toString(), r, (float)r * 37, (long)r);

                    }
                }
                /*try {
                    TimeUnit.NANOSECONDS.sleep(randomIndex);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
            }
        }
    }

    static class SomeObject<V> {
        V value;
        String stringField;
        double doubleField;
        float floatField;
        long longField;

        SomeObject(V value, String stringField, double doubleField, float floatField, long longField) {
            this.value = value;
            this.stringField = stringField;
            this.doubleField = doubleField;
            this.floatField = floatField;
            this.longField = longField;
        }

        @Override
        public String toString() {
            return "SomeObject{" +
                    "value=" + value == null ? "null" : "<obj>" +
                    ", stringField='" + stringField + '\'' +
                    ", doubleField=" + doubleField +
                    ", floatField=" + floatField +
                    ", longField=" + longField +
                    '}';
        }
    }
} 

- 但到目前为止没有效果,我尝试过使用Windows的不同2,4和8核心Intel / AMD PC,运行测试几个小时 - 没有重新排序效果 - System.err.printf(“观察者%s”看到...“) - 没有被调用,静态sharedArray [randomIndex]引用总是包含完全构造的值。

怎么了?如何重现这个?

4 个答案:

答案 0 :(得分:1)

无法保证重新订购。它通常仅在JIT确定可能存在性能增益时发生。

在您的情况下,您正在寻找的不是重新排序,而是一个似乎没有被正确初始化的对象。

您的对象很可能每次都使用新的内存位置(或者至少有一个不在缓存中)在x86 / x64架构上,我发现第一次加载时缓存始终是正确的内存何时被另一个线程更新。

答案 1 :(得分:1)

重新排序并不一定会发生。当它发生时,很难观察到。您的样本大小100太小了。为初学者尝试十亿。

假设重新排序发生,在写入时,首先分配引用,然后填充字段。您的读取过程首先读取引用,然后读取字段。因此,写入和读取遵循相同的顺序,读取几乎不可能赶上并先写入。

您可以通过

解决问题
  1. 在构造函数中暂停(因此暂停写入)
  2. 以相反方向写入和读取字段
  3. 尽管如此,重新排序可能不会发生,您将无法观察到它。

答案 2 :(得分:1)

这是一篇很好的文章,它应该显示在x86上的重新排序(因为x86内存模型非常“安全”),这是一个非常壮观的事情):

http://bartoszmilewski.wordpress.com/2008/11/05/who-ordered-memory-fences-on-an-x86/

您的示例不会显示重新排序。编译器不会重新排序为“在分配之后但在构造之前存储对象引用”,因为构造函数可能会抛出,因此需要还原引用。由于给定的保证,一些处理器可能会对此进行重新排序,但没有与intel兼容的处理器。

答案 3 :(得分:0)

您可以尝试将sharedArray声明为非易失性吗?在我看来,将易失性引用交给正确构造的对象是安全的。