214

同じクラスに2つの同期されたメソッドがあり、それぞれが異なる変数にアクセスしている場合、2つのスレッドがそれらの2つのメソッドに同時にアクセスできますか?ロックはオブジェクトで発生しますか、それとも同期メソッド内の変数と同じくらい具体的になりますか?

例:

class X {

    private int a;
    private int b;

    public synchronized void addA(){
        a++;
    }

    public synchronized void addB(){
        b++;
    }

}

x.addA(2つのスレッドが)を実行しているクラスXの同じインスタンスに同時にアクセスできますx.addB()か?

4

11 に答える 11

221

メソッドを同期済みとして宣言すると(入力して行うように)、オブジェクト全体public synchronized void addA()で同期するため、この同じオブジェクトから異なる変数にアクセスする2つのスレッドはとにかく互いにブロックします。

一度に1つの変数のみで同期する場合は、異なる変数にアクセスしているときに2つのスレッドが互いにブロックしないように、synchronized ()ブロックで別々に同期する必要があります。aおよびbがオブジェクト参照である場合は、次を使用します。

public void addA() {
    synchronized( a ) {
        a++;
    }
}

public void addB() {
    synchronized( b ) {
        b++;
    }
}

しかし、それらはプリミティブであるため、これを行うことはできません。

代わりにAtomicIntegerを使用することをお勧めします。

import java.util.concurrent.atomic.AtomicInteger;

class X {

    AtomicInteger a;
    AtomicInteger b;

    public void addA(){
        a.incrementAndGet();
    }

    public void addB(){ 
        b.incrementAndGet();
    }
}
于 2010-06-15T17:48:33.363 に答える
75

メソッド宣言で同期されるのは、このためのシンタックスシュガーです。

 public void addA() {
     synchronized (this) {
          a++;
     }
  }

静的メソッドでは、これは構文上の糖衣です。

 ClassA {
     public static void addA() {
          synchronized(ClassA.class) {
              a++;
          }
 }

Java設計者が同期について現在理解されていることを知っていれば、並行性の悪い実装につながることが多いため、構文上の糖衣を追加しなかったと思います。

于 2010-06-15T17:45:20.703 に答える
26

同期メソッドに関する「Java™チュートリアル」から:

まず、同じオブジェクトで同期されたメソッドを2回呼び出すことはできません。1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまで実行を一時停止します。

同期ブロックの「Java™チュートリアル」から:

同期されたステートメントは、きめ細かい同期で並行性を向上させるのにも役立ちます。たとえば、クラスMsLunchに2つのインスタンスフィールドc1とc2があり、これらが一緒に使用されることはないとします。これらのフィールドのすべての更新を同期する必要がありますが、c1の更新がc2の更新とインターリーブされるのを防ぐ理由はありません。同期すると、不要なブロッキングが作成され、同時実行性が低下します。同期メソッドを使用したり、これに関連付けられたロックを使用したりする代わりに、ロックを提供するためだけに2つのオブジェクトを作成します。

(エンファシスマイン)

2つの非インターリーブ変数があるとします。したがって、異なるスレッドから同時にそれぞれにアクセスする必要があります。オブジェクトクラス自体ではなく、以下のようにオブジェクトクラスにロックを定義する必要があります(2番目のOracleリンクの例)。

public class MsLunch {

    private long c1 = 0;
    private long c2 = 0;

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
}
于 2017-01-09T09:46:53.250 に答える
14

アクセスされるロックは、メソッドではなくオブジェクトにあります。メソッド内でどの変数にアクセスするかは関係ありません。

メソッドに「同期」を追加すると、コードを実行しているスレッドは、続行する前にオブジェクトのロックを取得する必要があります。「静的同期」を追加すると、コードを実行しているスレッドは、続行する前にクラスオブジェクトのロックを取得する必要があります。または、次のようにコードをブロックにラップすることもできます。

public void addA() {
    synchronized(this) {
        a++;
    }
}

これにより、ロックを取得する必要があるオブジェクトを指定できます。

含まれているオブジェクトのロックを回避したい場合は、次のいずれかを選択できます。

于 2010-06-15T17:39:39.483 に答える
7

オラクルのドキュメントリンクから

メソッドを同期させるには、次の2つの効果があります。

まず、同じオブジェクトで同期されたメソッドを2回呼び出すことはできません。1つのスレッドがオブジェクトの同期メソッドを実行している場合、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッドは、最初のスレッドがオブジェクトで完了するまで実行を一時停止します。

次に、同期されたメソッドが終了すると、同じオブジェクトに対する同期されたメソッドの後続の呼び出しとの発生前の関係が自動的に確立されます。これにより、オブジェクトの状態への変更がすべてのスレッドに表示されることが保証されます

固有のロックとロックの動作を理解するには、このドキュメントページを参照してください。

これはあなたの質問に答えます:同じオブジェクトxで、同期されたメソッドの実行の1つが進行中のときに、x.addA()とx.addB()を同時に呼び出すことはできません。

于 2016-02-21T10:12:15.453 に答える
4

同期されておらず、インスタンス変数にアクセスして変更しているメソッドがある場合。あなたの例では:

 private int a;
 private int b;

他のスレッドが同じオブジェクトの同期メソッド内にある場合、任意の数のスレッドがこれらの非同期メソッドに同時にアクセスでき、インスタンス変数に変更を加えることができます。たとえば:-

 public void changeState() {
      a++;
      b++;
    }

同期されていないメソッドがインスタンス変数にアクセスして変更するというシナリオを回避する必要があります。そうしないと、同期されたメソッドを使用しても意味がありません。

以下のシナリオでは:-

class X {

        private int a;
        private int b;

        public synchronized void addA(){
            a++;
        }

        public synchronized void addB(){
            b++;
        }
     public void changeState() {
          a++;
          b++;
        }
    }

addAまたはaddBメソッドに含めることができるスレッドは1つだけですが、同時に、任意の数のスレッドがchangeStateメソッドに入ることができます。(オブジェクトレベルのロックのために)2つのスレッドが同時にaddAとaddBに入ることができませんが、同時に任意の数のスレッドがchangeStateに入ることができます。

于 2015-08-07T10:57:35.827 に答える
4

この例(きれいな例ではありませんが)は、ロックメカニズムについてより多くの洞察を提供できます。増分A同期され、増分Bが同期されていない場合、増分Bはできるだけ早く実行されますが、増分Bも同期されている場合、増分Bがジョブを実行する前に、増分Aが終了するまで「待機」する必要があります。

両方のメソッドが単一のインスタンス(オブジェクト)に対して呼び出されます。この例では、 jobであり、「競合する」スレッドはaThreadmainです。

増分Bで「同期」を使用してみてください。そうでない場合は、異なる結果が表示されます。incrementBも「同期」されている場合は、 incrementA()が終了するまで待機する必要があります。各バリアントを数回実行します。

class LockTest implements Runnable {
    int a = 0;
    int b = 0;

    public synchronized void incrementA() {
        for (int i = 0; i < 100; i++) {
            this.a++;
            System.out.println("Thread: " + Thread.currentThread().getName() + "; a: " + this.a);
        }
    }

    // Try with 'synchronized' and without it and you will see different results
    // if incrementB is 'synchronized' as well then it has to wait for incrementA() to finish

    // public void incrementB() {
    public synchronized void incrementB() {
        this.b++;
        System.out.println("*************** incrementB ********************");
        System.out.println("Thread: " + Thread.currentThread().getName() + "; b: " + this.b);
        System.out.println("*************** incrementB ********************");
    }

    @Override
    public void run() {
        incrementA();
        System.out.println("************ incrementA completed *************");
    }
}

class LockTestMain {
    public static void main(String[] args) throws InterruptedException {
        LockTest job = new LockTest();
        Thread aThread = new Thread(job);
        aThread.setName("aThread");
        aThread.start();
        Thread.sleep(1);
        System.out.println("*************** 'main' calling metod: incrementB **********************");
        job.incrementB();
    }
}
于 2016-12-09T16:52:17.257 に答える
3

次のようなことができます。この場合、「this」のロックの代わりに、aとbのロックを使用して同期します。プリミティブ値にはロックがないため、intを使用できないため、整数を使用します。

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}
于 2010-06-15T18:05:10.490 に答える
3

はい、同期されたメソッドはポイントされたようにWHOLEクラスオブジェクトに適用されるため、他のメソッドをブロックします....しかし、とにかく、それが終了すると、それが入力するメソッドaddAまたはaddBで合計を実行している間のみ、他のスレッドの実行をブロックします...一方のスレッドはオブジェクトを解放し、もう一方のスレッドはもう一方のメソッドにアクセスするなど、完全に機能します

つまり、「同期」は、特定のコードの実行中に他のスレッドが別のスレッドにアクセスするのをブロックするために正確に作成されます。したがって、最終的にこのコードは正常に機能します。

最後に、一意の変数「a」やその他の名前だけでなく、「a」変数と「b」変数がある場合は、このメソッドを同期する必要はありません。他の変数(その他のメモリ)にアクセスしても完全に安全です。位置)。

class X {

private int a;
private int b;

public void addA(){
    a++;
}

public void addB(){
    b++;
}}

同様に機能します

于 2017-05-26T22:29:23.927 に答える
1

Java同期では、スレッドが同期メソッドを開始する場合、スレッドが使用している1つの同期メソッドだけでなく、そのオブジェクトのすべての同期メソッドのロックを取得します。したがって、addA()を実行するスレッドは、addA()とaddB()の両方が同期されるため、ロックを取得します。したがって、同じオブジェクトを持つ他のスレッドは、addB()を実行できません。

于 2019-05-20T03:02:50.600 に答える
0

整数から整数へのボクシングと自動ボクシングはJVMに依存しているため、これは機能しない可能性があります。また、2つの異なる数値が-128〜127の場合、同じアドレスにハッシュされる可能性が高くなります。

于 2015-07-29T21:36:12.743 に答える