6

私はこのようなサーバーを持っています:

class Server {

    private WorkingThing worker;

    public void init() {
         runInNewThread({
             // this will take about a minute
             worker = new WorkingThing();
         });
    }

    public Response handleRequest(Request req) {
         if (worker == null) throw new IllegalStateException("Not inited yet");
         return worker.work(req);
    }

}

ご覧のとおり、リクエストを処理するスレッドと、サーバーを開始するスレッドがあります。初期化が完了する前にリクエストが来る可能性があるため、IllegalStateException.

ここで、このスレッドを安全にするには (つまり、リクエスト ハンドラ スレッドがinit 直後の古いnull値のバージョンを認識しないようにするため)、worker を volatile にするか、同期するか、またはそのような作業を行う必要があります。worker

ただし、init が完了すると、worker再び変更されることはないため、事実上最終的なものになります。したがって、発生する可能性のあるロック競合は無駄に思えます。では、ここで私ができる最も効率的なことは何ですか?

実用的な意味ではあまり重要ではないことはわかっていますが (ネットワーク リクエストを読み取るなどの面倒な作業をすべて行うと、単一のロックがどのような意味を持つのでしょうか?)、好奇心から知りたいと思います。

4

4 に答える 4

3

volatile に関する注意: 変数を volatile とマークすることは、同期を使用するよりも安価であり (ロックは必要ありません)、一般的に気付かないほど安価です。特に、x86 アーキテクチャでは、揮発性変数の読み取りは、非揮発性変数の読み取りよりもコストがかかりません。ただし、揮発性への書き込みはより高価であり、変数が揮発性であるという事実により、一部のコンパイラーの最適化が妨げられる可能性があります。

したがって、volatile を使用することは、おそらく、シナリオで最高のパフォーマンス/複雑さの比率を実現するオプションです。

それほど多くの選択肢はありません。最終的には、ワーカーの安全な公開を保証することになります。また、安全な出版イディオムには次のものがあります。

  • 静的初期化子からのインスタンスの初期化
  • インスタンスへの参照を最終としてマークする
  • インスタンスへの参照を揮発性としてマークする
  • すべてのアクセスの同期

あなたの場合、最後の 2 つのオプションのみが利用可能であり、volatile を使用する方が効率的です。

于 2013-08-09T08:30:12.223 に答える
1

最初のリクエストには単純なロックを使用します。

public synchronized void init() {
    if(worker!=null) return;
    runInNewThread({
        synchronized(Server.this){
            worker = new WorkingThing();
            Server.this.notify();
        }
    });
    this.wait();
}

public Response handleRequest(Request req) {
    if (worker == null) synchronized(this) {
        this.wait();
    }
    return worker.work(req);
}

ワーカーへのアクセス間に同期ポイントがあるため、これが機能します。

于 2013-08-09T08:45:39.823 に答える
1

ワーカーがvolatileであることを宣言する必要があります。

2つの理由から

  1. 並べ替えと可視性の影響により、それを volatile として宣言しないと、不完全に構築されたワーカー オブジェクトへの非 null 参照が表示される可能性があります。したがって、望ましくない影響を与える可能性があります。すべての最終変数を使用してワーカーを不変にすると、これは発生しません。

  2. 理論的には、メイン スレッドがワーカー オブジェクトの非 null 参照を長時間認識しない可能性があります。だから避けてください。

したがって、ワーカーがポイント 2 よりも不変である場合は、揮発性にする必要があります。予測不可能な結果は常に避けてください。volatile と宣言すると、これらの問題に対処できます。

于 2013-08-09T08:30:53.650 に答える
0

null の場合は ava.util.concurrent.atomic.AtomicReference を使用するか、スローするか待機してから再試行します (init が十分に高速な場合)。

于 2013-08-09T08:34:56.807 に答える