synchronized用法まとめ


詳細
「プログラミング思想のマルチスレッドとマルチプロセス(1)」は、スレッド、プロセスの関係、オペレーティングシステムにおける表現を詳しく述べており、これはマルチスレッド学習が理解しなければならない基礎である.次に、Javaスレッド同期における重要な概念synchronizedについて説明する.
synchronizedはJavaのキーワードであり、同期ロックである.修飾するオブジェクトには、次のものがあります.1.修飾されたコードブロックを同期文ブロックと呼び、その作用範囲は括弧{}で囲まれたコードであり、作用するオブジェクトはこのコードブロックを呼び出すオブジェクトである.2.修飾された方法を同期方法と呼び、その作用の範囲は方法全体であり、作用するオブジェクトはこの方法を呼び出すオブジェクトである.3.静的方法を修正し、その作用範囲は静的方法全体であり、作用するオブジェクトはこのクラスのすべてのオブジェクトである.4.synchronizedの後ろに括弧で囲まれた部分で、役割主のオブジェクトがこのクラスのすべてのオブジェクトであるクラスを変更します.
コードブロックを修飾
1つのスレッドが1つのオブジェクトのsynchronized(this)同期コードブロックにアクセスすると、他のオブジェクトにアクセスしようとするスレッドがブロックされます.次の例を見てみましょう.です.
【Demo 1】:synchronizedの使い方
/**
 *     
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public  void run() {
      synchronized(this) {
         for (int i = 0; i < 5; i++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   public int getCount() {
      return count;
   }
}
 
SyncThreadの呼び出し:
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "SyncThread1");
Thread thread2 = new Thread(syncThread, "SyncThread2");
thread1.start();
thread2.start();
 
結果は次のとおりです.
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9*
 
2つの同時スレッド(thread 1およびthread 2)が同じオブジェクト(syncThread)のsynchronizedコードブロックにアクセスすると、同じ時点で1つのスレッドしか実行できず、もう1つのスレッドがブロックされ、現在のスレッドがこのコードブロックを実行してからコードブロックを実行するまで待たなければならない.Thread 1とthread 2は、synchronizedコードブロックを実行すると現在のオブジェクトがロックされ、コードブロックを実行してからオブジェクトロックが解放され、次のスレッドがオブジェクトを実行してロックできるため、反発します.SyncThreadの呼び出しを少し変更します.
Thread thread1 = new Thread(new SyncThread(), "SyncThread1");
Thread thread2 = new Thread(new SyncThread(), "SyncThread2");
thread1.start();
thread2.start();
 
結果は次のとおりです.
SyncThread1:0
SyncThread2:1
SyncThread1:2
SyncThread2:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread1:7
SyncThread1:8
SyncThread2:9
 
1つのスレッドがsynchronizedコードブロックを実行すると、他のスレッドがブロックされるのではないでしょうか.なぜ上記の例ではthread 1とthread 2が同時に実行されているのか.これはsynchronizedがオブジェクトのみをロックし、各オブジェクトにはロック(lock)が1つしか関連付けられていないためです.上のコードは次のコードと同じです.
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();
 
このとき、2つのSyncThreadのオブジェクトsyncThread 1とsyncThread 2が作成され、スレッドthread 1はsyncThread 1オブジェクトのsynchronizedコード(run)を実行し、スレッドthread 2はsyncThread 2オブジェクトのsynchronizedコード(run)を実行する.synchronizedがオブジェクトをロックしていることを知っています.この場合、syncThread 1オブジェクトとsyncThread 2オブジェクトをロックする2つのロックがありますが、この2つのロックは互いに干渉せず、反発を形成しないので、2つのスレッドは同時に実行できます.
2.あるスレッドがオブジェクトのsynchronized(this)同期コードブロックにアクセスする場合、別のスレッドは、オブジェクト内の非synchronized(this)同期コードブロックにアクセスすることができる.【Demo 2】:synchronizedおよび非synchronizedコードブロックに複数のスレッドがアクセス
class Counter implements Runnable{
   private int count;
 
   public Counter() {
      count = 0;
   }
 
   public void countAdd() {
      synchronized(this) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   // synchronized   ,  count      ,      synchronized
   public void printCount() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + " count:" + count);
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public void run() {
      String threadName = Thread.currentThread().getName();
      if (threadName.equals("A")) {
         countAdd();
      } else if (threadName.equals("B")) {
         printCount();
      }
   }
}
 
呼び出しコード:
Counter counter = new Counter();
Thread thread1 = new Thread(counter, "A");
Thread thread2 = new Thread(counter, "B");
thread1.start();
thread2.start();
 
結果は次のとおりです.
A:0
B count:1
A:1
B count:2
A:2
B count:3
A:3
B count:4
A:4
B count:5
 
上記のコードのcountAddはsynchronizedであり、printCountはsynchronizedではありません.上記の結果から、1つのスレッドが1つのオブジェクトのsynchronizedコードブロックにアクセスすると、他のスレッドは、ブロックされずにそのオブジェクトの非synchronizedコードブロックにアクセスできることがわかります.
オブジェクトにロックをかけるを指定します.
【Demo 3】:オブジェクトにロックをかけることを指定します
/**
 *      
 */
class Account {
   String name;
   float amount;
 
   public Account(String name, float amount) {
      this.name = name;
      this.amount = amount;
   }
   //  
   public  void deposit(float amt) {
      amount += amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   //  
   public  void withdraw(float amt) {
      amount -= amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
 
   public float getBalance() {
      return amount;
   }
}
 
/**
 *      
 */
class AccountOperator implements Runnable{
   private Account account;
   public AccountOperator(Account account) {
      this.account = account;
   }
 
   public void run() {
      synchronized (account) {
         account.deposit(500);
         account.withdraw(500);
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
      }
   }
}
 
呼び出しコード:
Account account = new Account("zhang san", 10000.0f);
AccountOperator accountOperator = new AccountOperator(account);
 
final int THREAD_NUM = 5;
Thread threads[] = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i ++) {
   threads[i] = new Thread(accountOperator, "Thread" + i);
   threads[i].start();
}
 
結果は次のとおりです.
Thread3:10000.0
Thread2:10000.0
Thread1:10000.0
Thread4:10000.0
Thread0:10000.0
 
AccountOperatorクラスのrunメソッドでは,synchronizedを用いてaccountオブジェクトにロックをかけた.この場合、1つのスレッドがaccountオブジェクトにアクセスすると、他のaccountオブジェクトにアクセスしようとするスレッドがブロックされ、そのスレッドがaccountオブジェクトにアクセスするまでブロックされます.つまり、誰がそのロックを手に入れたら、誰が制御しているコードを実行することができます.ロックとして明確なオブジェクトがある場合は、次のような方法でプログラムを書くことができます.
public void method3(SomeObject obj)
{
   //obj      
   synchronized(obj)
   {
      // todo
   }
}
 
ロックとして明確なオブジェクトがなく、コードを同期させたい場合は、ロックとして機能する特殊なオブジェクトを作成できます.
class Test implements Runnable
{
   private byte[] lock = new byte[0];  //    instance  
   public void method()
   {
      synchronized(lock) {
         // todo      
      }
   }
 
   public void run() {
 
   }
}
 
説明:ゼロ長のbyte配列オブジェクトを作成すると、どのオブジェクトよりも経済的になります.コンパイルされたバイトコードを表示します.ゼロ長のbyte[]オブジェクトを生成するには3つの操作コードしか必要ありません.Object lock=new Object()には7行の操作コードが必要です.
一つの方法を修飾する
Synchronized修飾の1つの方法は簡単で、方法の前にsynchronized、public synchronized void method()/todo}を加えることです.synchronized修飾方法は、1つのコードブロックを修飾するのと似ていますが、作用範囲が異なり、修飾コードブロックは括弧で囲まれた範囲であり、修飾方法範囲は関数全体です.【Demo 1】のrunメソッドを以下のように変更することで、実現する効果は同じです.
*【Demo 4】:synchronized修飾方法
public synchronized void run() {
   for (int i = 0; i < 5; i ++) {
      try {
         System.out.println(Thread.currentThread().getName() + ":" + (count++));
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}
 
Synchronizedはメソッド全体の書き方に作用します.書き方一:
 
public synchronized void method()
{
   // todo
}
 
書き方2:
public void method()
{
   synchronized(this) {
      // todo
   }
}
 
書き方一修飾は一つの方法であり、書き方二修飾はコードブロックであるが、書き方一と書き方二は等価であり、いずれも方法全体をロックした場合の内容である.
synchronizedで修飾する方法では、以下の点に注意してください.synchronizedキーワードは継承できません.synchronizedを使用してメソッドを定義できますが、synchronizedはメソッド定義の一部ではありません.したがって、synchronizedキーワードは継承できません.親クラスのメソッドでsynchronizedキーワードが使用され、子クラスでこのメソッドが上書きされている場合、子クラスのメソッドのデフォルトでは同期ではなく、子クラスのこのメソッドにsynchronizedキーワードを明示的に追加する必要があります.もちろん、子クラスメソッドで親クラスの対応するメソッドを呼び出すこともできます.これにより、子クラスのメソッドは同期ではありませんが、子クラスは親クラスの同期メソッドを呼び出すので、子クラスのメソッドも同期に相当します.この2つの方法の例コードは、サブクラスメソッドにsynchronizedキーワードを追加することです.
class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}
 
子メソッドで親を呼び出す同期メソッド
class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
}
 
インタフェースメソッドを定義するときにsynchronizedキーワードは使用できません.
構築方法ではsynchronizedキーワードは使用できませんが、synchronizedコードブロックを使用して同期できます.

静的な方法を修飾する
Synchronizedは、以下のように静的メソッドを修飾することもできます.
public synchronized static void method() {
   // todo
}
 
静的手法はオブジェクトではなくクラスに属することを知っている.同様にsynchronized修飾の静的メソッドは、このクラスのすべてのオブジェクトをロックします.Demo 1についていくつかの修正を行います.
【Demo 5】:synchronized修飾静的方法
/**
 *     
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}
 
呼び出しコード:
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();
 
結果は次のとおりです.
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
 
syncThread 1とsyncThread 2はsyncThreadの2つのオブジェクトですが、thread 1とthread 2が同時に実行されるとスレッド同期は維持されます.これはrunでは静的メソッドmethodが呼び出され,静的メソッドはクラスに属するため,syncThread 1とsyncThread 2は同じロックを用いたに相当する.これはDemo 1とは違います.
クラスを修飾する
Synchronizedは、次のように使用されるクラスにも使用できます.
class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}
 
私たちはDemo 5をもう少し修正します.【Demo 6】:クラスを修飾する
/**
 *     
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public static void method() {
      synchronized(SyncThread.class) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}
 
その効果は【Demo 5】と同じで、synchronizedが1つのクラスTに作用する場合、このクラスTにロックをかけ、Tのすべてのオブジェクトは同じロックを使う.
まとめ:
A.synchronizedキーワードがメソッドに追加されてもオブジェクトに追加されても、その作用するオブジェクトが非静的である場合、その取得したロックはオブジェクトである.synchronizedが作用するオブジェクトが静的メソッドまたはクラスである場合、取得したロックはクラスであり、そのクラスのすべてのオブジェクトが同じロックである.B.オブジェクトごとに1つのロック(lock)のみが関連付けられており、このロックを手に入れると、その制御されたコードを実行することができる.C.同期を実現するには、システムオーバーヘッドが大きいことを代価とし、デッドロックをもたらす可能性もあるので、無駄な同期制御をできるだけ避ける.
 
 
変換元:http://www.importnew.com/21866.html