1

Java 1.4 API を使用したXletの開発に携わっています。

ドキュメントによると、Xletインターフェイス メソッド (実際には xlet ライフサイクル メソッド) は、その特別なスレッド (EDT スレッドではありません) で呼び出されます。ログで確認しました-これは本当です。EDT でライフサイクル メソッドが呼び出される BB/Android フレームワークとは異なるため、これは私にとっては少し意外ですが、今のところ問題ありません。

プロジェクト コードでは、アプリが広範囲に呼び出しを使用していることがわかります (これは、EDT スレッドDisplay.getInstance().callSerially(Runnable task)で実行する LWUIT の方法です)。Runnable

したがって、基本的に、Xlet 実装クラス内のコードの一部は、EDT スレッドから xlet 内部状態オブジェクトの作成/更新/読み取り操作を実行し、他の一部のコードは、同期なしでライフサイクル スレッドから実行します (状態変数が同期されていないことを含む)。 volatile として宣言されています)。次のようにします。

class MyXlet implements Xlet {

    Map state = new HashMap();

    public void initXlet(XletContext context) throws XletStateChangeException {
        state.put("foo", "bar"); // does not run on the EDT thread

        Display.getInstance().callSerially(new Runnable() {
            public void run() {
                // runs on the EDT thread
                Object foo = state.get("foo");
                // branch logic depending on the got foo
            }
        });
    }

    ..
}

私の質問は次のとおりです。これにより、まれな並行性の問題の背景が作成されますか? 状態へのアクセスを明示的に同期する必要がありますか (または、少なくとも状態を揮発性として宣言する必要があります)?

私の推測では、コードがマルチコア CPU で実行されるかどうかによって異なります。マルチコア CPU では、2 つのスレッドが独自のコアで実行されている場合、変数がキャッシュされるため、各スレッドが明示的に同期されない限り、状態の独自のバージョン。

私の懸念に対して信頼できる回答を得たいと思います。

4

1 に答える 1

2

はい、説明したシナリオでは、共有状態へのアクセスをスレッドセーフにする必要があります。

注意が必要な問題が 2 つあります。

最初の問題である可視性 (既に述べた) は、ユニプロセッサでも発生する可能性があります。問題は、JIT コンパイラーが変数をレジスターにキャッシュできることと、コンテキスト・スイッチで OS がレジスターの内容をスレッド・コンテキストにダンプして、後で再開できるようにすることです。ただし、これはレジスタの内容をオブジェクトのフィールドに書き戻すことと同じではないため、コンテキストの切り替え後にオブジェクトのフィールドが最新であるとは想定できません。

たとえば、次のコードを見てください。

class Example {
    private int i;

    public void doSomething() {
        for (i = 0; i < 1000000; i ++) {
            doSomeOperation(i);
        }
    }
}

ループ変数 (インスタンス フィールド)iは volatile として宣言されていないため、JIT はiCPU レジスタを使用してループ変数を最適化できます。これが発生した場合i、ループが完了するまで、JIT はレジスタの値をインスタンス変数に書き戻す必要はありません。

したがって、スレッドが上記のループを実行していて、それが横取りされたとしましょう。の最新の値がレジスタにあり、そのレジスタがスレッド ローカル実行コンテキストに保存されているiため、新しくスケジュールされたスレッドは の最新の値を確認できません。i少なくとも、インスタンス フィールドiを宣言volatileして、 の各更新iを他のスレッドから見えるようにする必要があります。

2 つ目の問題は、オブジェクトの状態の一貫性です。HashMapコード内の を例にとると、内部的にはいくつかの非最終メンバー変数、sizetableおよびthresholdで構成されていmodCountます。リンクされたリストを形成するtableの配列はどこにありますか。Entry要素がマップに追加されたり、マップから削除されたりする場合、状態の一貫性を保つために、これらの状態変数の 2 つ以上をアトミックに更新する必要があります。HashMapこれは、アトミックであるためには、ブロック内または同様のもの内で実行する必要がsynchronizedあります。

2 番目の問題については、ユニプロセッサで実行すると問題が発生します。これは、OS または JVM が、現在のスレッドが put メソッドまたは remove メソッドの実行の途中でスレッドをプリエンプティブに切り替えてから、同じ に対して他の操作を実行しようとする別のスレッドに切り替える可能性があるためHashMapです。

EDT スレッドが「get」メソッドを呼び出している最中にプリエンプティブ スレッド スイッチが発生し、別のエントリをマップに挿入しようとするコールバックを受け取った場合にどうなるか想像してみてください。しかし今回は、マップが負荷係数を超えたため、マップのサイズが変更され、すべてのエントリが再ハッシュされて挿入されました。

于 2012-11-05T15:42:04.623 に答える