web-dev-qa-db-ja.com

Javaオブジェクトの同期メソッドロック、またはメソッド?

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

例:

class X {

    private int a;
    private int b;

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

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

}

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

168
wuntee

メソッドを同期化済みとして宣言すると(public synchronized void addA()と入力して行うように)wholeオブジェクトで同期するため、この同じオブジェクトから異なる変数にアクセスする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();
    }
}
170
OscarRyz

メソッド宣言で同期されるのは、このための構文上の砂糖です。

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

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

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

Javaデザイナーが同期について現在理解していることを知っていれば、同時性の悪い実装につながることが多いため、構文上の砂糖を追加しなかったでしょう。

59
Yishai

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

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

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

そのため、ロックを取得する必要があるオブジェクトを指定できます。

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

13
Nathan Hughes

Oracleのドキュメントから link

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

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

第二に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しと発生前の関係を自動的に確立します。これにより、オブジェクトの状態への変更がすべてのスレッドから見えるようになります。

このドキュメントを参照してください page 固有のロックとロックの動作を理解してください。

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

4
Aditya W

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

class x{
   private Integer a;
   private Integer b;
   public void addA(){
      synchronized(a) {
         a++;
      }
   }
   public synchronized void addB(){
      synchronized(b) {
         b++;
      }
   }
}
2
dsmith

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

 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に入ることができます。

2
Goyal Vicky

この例(とは言えませんが)は、ロックメカニズムについてより多くの洞察を提供できます。 incrementA同期であり、incrementBの場合is not synchronized、thenincrementBはできるだけ早く実行されますが、incrementB同期であるため、incrementAが完了するまで「=」待機してからincrementBはその仕事をすることができます。

両方のメソッドは単一のインスタンス-オブジェクトに呼び出されます。この例では、jobであり、「競合」スレッドはaThreadおよびmain.

incrementBで 'synchronized'を試してみて、それなしで別の結果が表示されます。IfincrementBも 'synchronized'であり、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();
    }
}
1
Nenad Bulatovic

はい、同期メソッドはWHOLEクラスオブジェクトに適用されるため、他のメソッドをブロックします....しかし、とにかく他のスレッドの実行をブロックしますONLY =入るメソッドaddAまたはaddBで合計を実行している間、終了すると... 1つのスレッドがFREEオブジェクトと他のスレッドが他のメソッドなどに完全にアクセスするためワーキング。

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

最後の注意点として、一意の変数「a」または他の名前だけでなく、「a」および「b」変数がある場合、このメソッドを同期する必要はないため、他の変数(他のメモリロケーション)。

class X {

private int a;
private int b;

public void addA(){
    a++;
}

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

同様に動作します

1
Jose Velandia

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

0
Sreedhar Reddy

整数から整数およびその逆へのボクシングとオートボクシングはJVMに依存しているため、これは機能しない可能性があり、2つの異なる数値が-128と127の間の場合、同じアドレスにハッシュされる可能性が高いです。

0
Sriharsha g.r.v