17

Java では、変数のサイズが 32 ビット以下の場合、割り当てはアトミックですが、32 ビットを超える場合はそうではありません。

二重割り当てまたは長い割り当ての場合、どの (揮発性/同期) を使用するのがより効率的ですか?

お気に入り、

  volatile double x = y;

synchronized はプリミティブ引数には適用できません。この場合、同期を使用するにはどうすればよいですか? もちろん、クラスをロックしたくないので、this使用しないでください。

4

5 に答える 5

25

あなたは何をしようとしているのですか?synchronizedandキーワードは Javaのvolatileメカニズムであり、同じデータを読み取るさまざまなスレッドで一貫した値が観察されるようにするために使用できます。特に、プログラム内の事前発生関係について推論することができます。

マルチスレッド プログラムで非フィールドに適切にアクセスするために、volatileまたはのいずれかを使用することを避けることはできません。そうは言っても、必要になる可能性が高い主な理由は、アトミックな比較および設定操作を使用するための要件です (つまり、パフォーマンスの考慮事項ではありません)。たとえば、マルチスレッド プログラムでは次のようになります。synchronizedfinalsynchronizedvolatile

volatile int i = 0;

public void foo() { 
    if (i == 0) i = i + 1;
}

上記のコードは本質的に安全ではありませんが、変数の宣言が volatile であることは、読み取りと書き込みがメイン メモリにフラッシュされることを意味します。そのようなメソッドの唯一の安全な実装は、次のようになります。

int i = 0;

public synchronized void foo() {
    if (i == 0) i = i + 1;
}

それで、あなたはどちらを好むべきですか?そのフィールドの値に依存するフィールドを変更する複数のスレッドがある場合 (つまり、比較と設定)、それsynchronizedが唯一の安全な解決策です。

また、言う価値があります:のパフォーマンス オーバーヘッドはsynchronized問題ではありません(圧倒的多数の場合)。同期パフォーマンスの問題は通常、不要なコードのボトルネック、デッドロック、またはライブロックが原因であり、必要に応じて軽減できます。純粋なクロック サイクルのオーバーヘッドは、アプリケーションが行う他のこと (ファイル IO、データベース クエリ、リモーティングなど) によって小さくなります。

于 2009-11-22T17:05:14.957 に答える
5

オブジェクト自体のロックが重すぎる場合は、同期が最適です。Java 1.5 より前は、volatile が適切な選択であった可能性がありますが、現在、volatile は、割り当てが発生するメソッドで命令の順序付けを強制することにより、非常に大きな影響を与える可能性があります。別のオブジェクト ( private final Object X_LOCK = new Object();) を作成し、その double の値を設定または取得するときに同期します。これにより、必要と思われるロックを細かく制御できます。

新しい同時実行パッケージには、同期を本当に回避する必要がある場合に volatile の適切な代替となる AtomicReference など、より多くのオプションがあります。

于 2009-11-22T17:11:41.020 に答える
3

volatile は、割り当てのみを行う場合に最適な方法です。ご存知だと思いますが、それが持ち出されたので、より複雑な操作(たとえば、値を増やす)を行いたい場合は、同期する必要があります。i++ は、どのタイプの変数に対してもスレッドセーフではありません。同期する必要があります。i++ などは、実際には複数の操作であるためです。

注: AtomicDouble を使用できるとのことでしたが、現在 java.util.concurrent.atomic には AtomicDouble はありません。

x に対して複数の操作を行っている場合、最後に新しい値を設定する必要があります。これをロックせずにスレッド セーフな方法で実行し、compare を使用してスレッド セーフにすることができます。設定。例:

AtomicLong x = new AtomicLong(SomeValue);
public void doStuff() {
  double oldX;
  double newX;
  do {
    oldX = x.get();
    newX = calculateNewX(oldX);
  } while (!x.compareAndSet
      (Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX)));

これが機能するのは、compareAndSet が x の値が最後に読み取ってから変更されたかどうかを確認するためです。x が変更された場合、計算をやり直して設定し直す必要があります。

もちろん、これらの doubleToLongBits 変換を行う代わりに、独自の AtomicDouble を実装することもできます。AtomicFieldUpdater を見てください。

于 2009-11-23T11:15:25.017 に答える
2

KDSRathore、明示的なロックを使用するか、ダミーの Object object = new Object() を作成して、そのダブルのセッター/ゲッターで同期することができます

于 2009-11-22T17:06:57.507 に答える
1

オラクルのドキュメントによると、 volatile を使用して Double オブジェクトを参照できます。

volatile Double x = y;

「参照への書き込みと参照の読み取りは、それらが 32 ビットまたは 64 ビットの値として実装されているかどうかに関係なく、常にアトミックです。」

于 2017-12-08T11:26:20.467 に答える