3

たとえば、(マルチスレッド環境で) 2 つのカウンターを持つクラスがあります。

public class MyClass {
  private int counter1;
  private int counter2;

  public synchronized void increment1() {
        counter1++;
  }

  public synchronized void increment2() {
        counter2++;
  }

}

相互に関係のない2 つのインクリメント操作があります。しかし、ロックには同じオブジェクトを使用します ( this)。

クライアントがメソッドとメソッドを同時に呼び出すincrement1()と、モニターが解放されるまで呼び出しがブロックされるのは本当ですか?increment2()increment2increment1()this

それが本当なら、 (パフォーマンス上の理由から) 操作ごとに異なるモニター ロックを提供する必要があるということですか?

4

7 に答える 7

12

クライアントがincrement1()メソッドとincrement2()メソッドを同時に呼び出す場合、increment1()がこのモニターを解放するまでincrement2の呼び出しがブロックされるのは事実ですか?

それらが同じインスタンスで呼び出された場合は、はい。

それが本当なら、それは私が(パフォーマンス上の理由で)操作ごとに異なるモニターロックを提供する必要があることを意味しますか?

あなただけがそれを知ることができます。お客様のパフォーマンス要件はわかりません。これは実際に実際のコードの問題ですか?あなたの実際の操作は長続きしますか?それらは非常に頻繁に発生しますか?これの影響を推定するために診断を実行しましたか?アプリケーションのプロファイルを作成して、モニターの待機にどれだけの時間が費やされているかを調べましたか。不要な場合は言うまでもありません。

実際にはthis、まったく異なる理由で同期しないことをお勧めします。すべてを制御するときにスレッド化について推論するのはすでに十分に困難ですが、モニターを取得できるすべてのものがわからない場合は、何も隠されていません。で同期すると、オブジェクトへの参照を持つ他のコードも同じモニターで同期できることをthis意味します。たとえば、クライアントは次を使用できます。

synchronized (myClass) {
    // Do something entirely different
}

これは、デッドロック、パフォーマンスの問題、あらゆる種類のものにつながる可能性があります。

代わりにクラスのフィールドを使用し、モニターとしてのみ作成されたオブジェクトを使用する場合private finalそのモニターを取得する唯一のコードがコードになることがわかります。

于 2013-01-29T07:19:59.747 に答える
5

1)はい、increment1()がincrement2()をブロックし、その逆も同様です。どちらも暗黙的に同期しているためです。this

2)より良いパフォーマンスが必要な場合は、ロックフリーのjava.util.concurrent.atomic.AtomicIntegerクラスを検討してください。

  private AtomicInteger counter1 = new AtomicInteger();
  private AtomicInteger counter2 = new AtomicInteger();

  public void increment1() {
        counter1.getAndIncrement();
  }

  public void increment2() {
        counter2.getAndIncrement();
  }
于 2013-01-29T07:21:28.257 に答える
2

同じオブジェクトを使用すると、パフォーマンスのボトルネックになります。個々のカウンターに異なるロックを使用することもjava.util.concurrent.atomic.AtomicInteger、同時カウンターに使用することもできます。

好き:

public class Counter {
  private AtomicInteger count = new AtomicInteger(0);
  public void incrementCount() {
    count.incrementAndGet();
  }
  public int getCount() {
    return count.get();
  }
}
于 2013-01-29T07:20:54.370 に答える
2

ここで行ったように、メソッドを同期すると、オブジェクト全体がロックされるため、この同じオブジェクトから異なる変数にアクセスする2つのスレッドは、とにかく互いにブロックします。

一度に1つのカウンターのみを同期して、2つのスレッドが異なる変数にアクセスしているときに互いにブロックしないようにする場合は、ここで2つのカウンターを2つの同期ブロックに追加し、異なる変数を2つの「ロック」として使用する必要があります。ブロック。

于 2013-01-29T07:19:03.097 に答える
1

はい、複数のスレッドがオブジェクトのメソッドを呼び出そうとすると、ロックの取得を待機します(ただし、ロックを取得する順序は保証されません)。すべての場合と同様に、これがボトルであることがわかるまで、最適化する理由はありません。あなたのコードの首。

于 2013-01-29T07:20:43.097 に答える
1

はい、指定されたコードは次のものと同じです。

  public void increment1() {
        synchronized(this) {
             counter1++;
        }
  }

  public oid increment2() {
        synchronized(this) {
             counter2++;
        }
  }

つまり、同時に実行できるメソッドは1つだけです。別のロックを提供するか(最初はロックをオンにthisすることはお勧めできません)、または他の解決策を提供する必要があります。2つ目は、実際にここで必要なものです:AtomicInteger

于 2013-01-29T07:21:19.660 に答える
0

両方の操作を並行して呼び出すことができることで得られるパフォーマンス上の利点が必要な場合は、そうです。異なる操作に異なるモニターオブジェクトを提供する必要はありません。

ただし、時期尚早の最適化については、プログラムをより複雑にしてそれに対応する前に、それが必要であることを確認する必要があります。

于 2013-01-29T07:19:39.087 に答える