5

いくつかのJava記事を書いている間、私はマルチスレッド環境で非同期オブジェクトのコスト削減の場合の並べ替えを再現しようとしています。重いオブジェクトがsynchonization/volatiles / finalsなしで構築され、他のスレッドがコンストラクター呼び出しの直後にそのオブジェクトにアクセスする場合。これが私が試したコードです:

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( "watcher%s sees.. 。")-呼び出されません。静的sharedArray [randomIndex]参照には、常に完全に構​​築された値が含まれます。

どうしたの?これを再現する方法は?

4

4 に答える 4

1

再注文は保証されません。これは通常、JITがパフォーマンスの向上があると判断した場合にのみ発生します。

あなたの場合、あなたが探しているのは並べ替えではなく、正しく初期化されていないように見えるオブジェクトです。

オブジェクトは毎回新しいメモリ位置(またはキャッシュにない少なくとも1つ)を使用する可能性が高いです。x86/ x64アーキテクチャでは、最初にメモリをロードする必要があるときにキャッシュが常に正しいことがわかりました。別のスレッドによって更新されました。

于 2011-06-07T11:47:32.043 に答える
1

再注文は必ずしも発生しません。そうなると、観察するのが難しくなります。のサンプルサイズ100が小さすぎます。手始めに10億を試してみてください。

書き込み時に、最初に参照が割り当てられ、次にフィールドにデータが入力される、並べ替えが発生したとします。読み取りプロセスは、最初に参照を読み取り、次にフィールドを読み取ります。したがって、書き込みと読み取りは同じ順序に従います。読み取りが追いついて書き込みに先んじることはほとんど不可能です。

あなたはによって問題に対処することができます

  1. コンストラクターで一時停止(したがって、書き込みで一時停止)
  2. 反対方向のフィールドの書き込みと読み取り

それでも、並べ替えが行われない可能性があり、それを確認することはできません。

于 2011-06-07T12:22:42.293 に答える
1

これは、x86での並べ替えを示すはずの素晴らしい記事です(x86メモリモデルはほとんど「安全」であるため、かなりの偉業です)。

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

あなたの例は並べ替えを示していません。コンストラクターは、コンストラクターがスローする可能性があり、したがって参照を元に戻す必要があるため、「割り当て後、構築前にオブジェクト参照を格納する」ように並べ替えることはありません。一部のプロセッサはこれを再注文する場合がありますが、所定の保証により、Intel互換のものはありません。

于 2011-06-07T13:18:50.670 に答える
0

sharedArray不揮発性として宣言してみてください。正しく構築されたオブジェクトへの揮発性参照を渡すことは安全であるように私には思えます。

于 2011-06-07T11:44:38.567 に答える