Java synchronizedキー_動力ノードJava学院の整理

20408 ワード

synchronized原理
javaには、各オブジェクトがあり、同期鍵が一つしかない。これは、同期ロックがオブジェクトに依存して存在することを意味する。
あるオブジェクトのsynchronizedメソッドを呼び出すと、そのオブジェクトの同期ロックが取得されます。例えば、synchronized(obj)は、「obj」というオブジェクトの同期ロックを取得する。
同期ロックに対する異なるスレッドのアクセスは互いに反発する。つまり、ある時点で、オブジェクトの同期ロックは一つのスレッドでしか取れないということです。同期ロックにより、マルチスレッド内で「オブジェクト/メソッド」への相互反発訪問を実現することができます。例えば、2つのスレッドAとスレッドBがあり、それらはいずれも「オブジェクトobjの同期ロック」にアクセスする。スレッドAは、ある時点で「Objの同期ロック」を取得し、いくつかの動作を実行していると仮定する。このとき、スレッドBは、「Objの同期ロック」を取得しようとしています。スレッドBは、スレッドAが「このオブジェクトの同期ロック」をリリースした後に、スレッドBが「Objの同期ロック」を取得するまで待機しなければなりません。 
synchronized基本ルール
synchronizedの基本的なルールを次の3つにまとめ、それらを実例によって説明します。
第一条:スレッドが「ある対象」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドの「その対象」に対する当該「synchronized方法」または「synchronizedコードブロック」へのアクセスが遮断されます。
第二条:スレッドが「あるオブジェクト」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドは依然として「このオブジェクト」の非同期コードブロックにアクセスすることができる。
第三条:スレッドが「ある対象」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドが「その対象」の他の「synchronized方法」または「synchronizedコードブロック」へのアクセスが遮断されます。 
第一条
スレッドが「ある対象」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドの「その対象」に対する当該「synchronized方法」または「synchronizedコードブロック」へのアクセスが遮断されます。
以下は「synchronizedコードブロック」に対応するプレゼンテーションプログラムです。 

 class MyRunable implements Runnable {  
  @Override
  public void run() {
   synchronized(this) {
    try { 
     for (int i = 0; i < 5; i++) {
      Thread.sleep(); //   ms
      System.out.println(Thread.currentThread().getName() + " loop " + i); 
     }
    } catch (InterruptedException ie) { 
    }
   } 
  }
 }

public class Demo_ {
  public static void main(String[] args) { 
   Runnable demo = new MyRunable();  //   “Runnable  ”
   Thread t1 = new Thread(demo, "t1"); //   “  t1”, t1   demo  Runnable  
  Thread t2 = new Thread(demo, "t2"); //   “  t2”, t2   demo  Runnable  
   t.start();       //   “  t”
   t.start();       //   “  t” 
  } 
 }
実行結果: 
t 1 loop 0
t 1 loop 1
t 1 loop 2
t 1 loop 3
t 1 loop 4
t 2 loop 0
t 2 loop 1
t 2 loop 2
t 2 loop 3
t 2 loop 4 
結果説明:
run()メソッドには「synchronizedコードブロック」があり、t 1とt 2は「demo」というRunnableオブジェクトに基づいて作成されたスレッドである。これは、synchronizedの中のthisを「demoというRunnableの対象」と見なしても良いという意味です。したがって、スレッドt 1とt 2は、「デモオブジェクトの同期ロック」を共有する。したがって、スレッドが実行されると、もう一つのスレッドは「実行スレッド」がリリースされるのを待つ必要があります。
もしあなたが確認したら、この問題が分かりました。上のコードを修正して運行してみます。結果はどうなりますか?修正後のソースコードは以下の通りです。 

class MyThread extends Thread {
  public MyThread(String name) {
   super(name);
  }
  @Override
  public void run() {
   synchronized(this) {
    try { 
     for (int i = 0; i < 5; i++) {
      Thread.sleep(100); //   100ms
      System.out.println(Thread.currentThread().getName() + " loop " + i); 
     }
    } catch (InterruptedException ie) { 
    }
   } 
  }
 }
 public class Demo1_2 {
  public static void main(String[] args) { 
   Thread t1 = new MyThread("t1"); //   “  t1”
  Thread t2 = new MyThread("t2"); //   “  t2”
   t.start();       //   “  t”
   t.start();       //   “  t” 
  } 
 }
コードの説明:
比較Demo 1_2とDemo 1_1,私達はDemo 1_を発見しました。2のMyThread類はそのままThreadに継承され、t 1とt 2はMyThread子スレッドである。
幸いなことに、「デモ1_」では2のrun()メソッドも、synchronized(this)を呼び出します。1のrun()メソッドも、synchronized(this)を呼び出しました!
では、デモ1_2の実行フローはDemo 1_と同じですか?1同じですか?
実行結果: 
t 1 loop 0
t 2 loop 0
t 1 loop 1
t 2 loop 1
t 1 loop 2
t 2 loop 2
t 1 loop 3
t 2 loop 3
t 1 loop 4
t 2 loop 4
結果説明:
この結果が少しも驚かないなら、あなたのsynchronizedとthisに対する認識はもう深くなったと思います。さもないと、ここの分析を続けて読んでください。
synchronized(this)のthisは、「現在のクラスのオブジェクト」であり、synchronized(this)があるクラスに対応する現在のオブジェクトです。その役割は「現在のオブジェクトの同期ロック」を取得することです。
Demo 1_に対して2において、synchronizedのthisは、MyThreadオブジェクトを表し、t 1とt 2は2つの異なるMyThreadオブジェクトであるため、t 1とt 2は、synchronized(this)を実行する際に、異なるオブジェクトの同期ロックを取得する。Demo 1_に対して1対にとって、synchronizedの中のthisはMyRunableオブジェクトを表しています。t 1とt 2は同じMyRunableオブジェクトであるため、一つのスレッドが対象の同期ロックを取得し、他のスレッドが待機することになる。 
第二条
スレッドが「あるオブジェクト」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドは依然として「このオブジェクト」の非同期コードブロックにアクセスすることができる。
以下は「synchronizedコードブロック」に対応するプレゼンテーションプログラムです。 

 class Count {
  //   synchronized      
  public void synMethod() {
   synchronized(this) {
    try { 
     for (int i = 0; i < 5; i++) {
     Thread.sleep(100); //   100ms
      System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); 
     }
    } catch (InterruptedException ie) { 
    }
   } 
  }
  //       
  public void nonSynMethod() {
   try { 
    for (int i = 0; i < 5; i++) {
     Thread.sleep(100);
     System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); 
    }
   } catch (InterruptedException ie) { 
   }
  }
 }
 public class Demo {
  public static void main(String[] args) { 
   final Count count = new Count();
   //   t, t   “count  ” synMethod()  
   Thread t = new Thread(
     new Runnable() {
      @Override
      public void run() {
       count.synMethod();
      }
    }, "t1");
  //   t2, t2   “count  ” nonSynMethod()  
  Thread t2 = new Thread(
     new Runnable() {
      @Override
      public void run() {
       count.nonSynMethod();
      }
     }, "t2"); 
  t1.start(); //   t1
   t2.start(); //   t2
  } 
 }
実行結果: 
t 1 synMethod loop 0
t 2 non SyncMethod loop 0
t 1 synMethod loop 1
t 2 non SyncMethod loop 1
t 1 synMethod loop 2
t 2 non SyncMethod loop 2
t 1 synMethod loop 3
t 2 non SyncMethod loop 3
t 1 synMethod loop 4
t 2 non SyncMethod loop 4 
結果説明:
メインスレッドには、2つのサブスレッドt 1とt 2が新規に作成されている。t 1は、同期ブロックを含むcountオブジェクトのsynMethod()方法を呼び出す。t 2は、countオブジェクトのnonSyncMethod()方法を呼び出す。この方法は同期方法ではない。t 1運転時は、synchronizedを呼び出しますが、「countの同期ロック」を取得します。t 2は「count」同期ロックを使用していないので、t 2の渋滞は起きていません。
第三条
スレッドが「ある対象」の「synchronized方法」または「synchronizedコードブロック」にアクセスすると、他のスレッドの「その対象」に対する他の「synchronized方法」または「synchronizedコードブロック」へのアクセスが遮断されます。
上記の例のnon Synchronized(this)によっても修正します。修正後のソースコードは以下の通りです。 

 class Count {
  //   synchronized      
  public void synMethod() {
   synchronized(this) {
    try { 
     for (int i = 0; i < 5; i++) {
      Thread.sleep(); //   ms
      System.out.println(Thread.currentThread().getName() + " synMethod loop " + i); 
     }
    } catch (InterruptedException ie) { 
    }
   } 
  }
  //    synchronized      
  public void nonSynMethod() {
   synchronized(this) {
    try { 
    for (int i = 0; i < 5; i++) {
      Thread.sleep(100);
      System.out.println(Thread.currentThread().getName() + " nonSynMethod loop " + i); 
     }
    } catch (InterruptedException ie) { 
    }
   }
  }
 }
 public class Demo3 {
  public static void main(String[] args) { 
   final Count count = new Count();
   //   t1, t1   “count  ” synMethod()  
  Thread t1 = new Thread(
     new Runnable() {
      @Override
      public void run() {
       count.synMethod();
      }
    }, "t1");
  //   t2, t2   “count  ” nonSynMethod()  
   Thread t2 = new Thread(
     new Runnable() {
      @Override
      public void run() {
       count.nonSynMethod();
      }
     }, "t2"); 
  t1.start(); //   t1
  t2.start(); //   t2
  } 
 }
実行結果: 
t 1 synMethod loop 0
t 1 synMethod loop 1
t 1 synMethod loop 2
t 1 synMethod loop 3
t 1 synMethod loop 4
t 2 non SyncMethod loop 0
t 2 non SyncMethod loop 1
t 2 non SyncMethod loop 2
t 2 non SyncMethod loop 3
t 2 non SyncMethod loop 4 
結果説明:
メインスレッドには、2つのサブスレッドt 1とt 2が新規に作成されている。t 1とt 2の運転時には、synchronizedを呼び出し、このthisはCountオブジェクトであり、t 1とt 2はcountを共有する。したがって、t 1運転時にt 2がブロックされ、t 1運転が「countオブジェクトの同期ロック」を解放するのを待って、t 2が動作する。 
synchronizedメソッドとsynchronizedコードブロック
「synchronizedメソッド」はsynchronizedで修飾されますが、「synchronizedコードブロック」はsynchronizedでコードブロックを修飾します。
synchronized方法の例

public synchronized void foo1() {
 System.out.println("synchronized methoed");
}
synchronizedコードブロック

public void foo2() {
 synchronized (this) {
  System.out.println("synchronized methoed");
 }
}
synchronizedコードブロックのthisとは、現在のオブジェクトのことです。thisを他のオブジェクトに置き換えることもでき、例えば、thisをobjに置き換えると、foo 2()は、synchronized(obj)を実行する際に取得されるのは、Objの同期ロックである。
synchronizedコードブロックは、衝突制限アクセス領域をより正確に制御することができ、場合によってはより効率的に表現することができる。以下では一例を通して説明します。 

// Demo4.java   
 public class Demo4 {
  public synchronized void synMethod() {
  for(int i=0; i<1000000; i++)
    ;
  }
  public void synBlock() {
   synchronized( this ) {
    for(int i=0; i<1000000; i++)
     ;
   }
  }
  public static void main(String[] args) {
   Demo4 demo = new Demo4();
   long start, diff;
   start = System.currentTimeMillis();    //       (millis)
   demo.synMethod();        //   “synchronized  ”
   diff = System.currentTimeMillis() - start;  //   “    ”
   System.out.println("synMethod() : "+ diff);
   start = System.currentTimeMillis();    //       (millis)
   demo.synBlock();        //   “synchronized   ”
   diff = System.currentTimeMillis() - start;  //   “    ”
   System.out.println("synBlock() : "+ diff);
  }
 }
(一回)実行結果:
synMethod():11
synBlock():3 
インスタンスロックとグローバルロック
インスタンスロック--あるインスタンスオブジェクトにロックします。このクラスが単一の例であるならば、このロックも大域的なロックの概念を有する。
               インスタンスロックに対応するのが、synchronizedキーワードです。
大域ロックは、クラスに対してロックされ、いくつかのオブジェクトの例に関わらずスレッドが共有される。
               グローバルロックは、static synchronizedに対応しています。
「インスタンスロック」と「グローバルロック」についてイメージした例があります。

pulbic class Something {
 public synchronized void isSyncA(){}
 public synchronized void isSyncB(){}
 public static synchronized void cSyncA(){}
 public static synchronized void cSyncB(){}
}
Somethingには2つのインスタンスxとyがあると仮定する。次の4つの式で取得した鍵の場合を分析します。
(01)x.isSyncA()とx.isSyncB()
(02)x.isSyncA()とy.isSyncA()
(03)x.cyncA()とy.cyncB()
(04)x.isSyncA()とSomething.cyncA()
(01)同時にアクセスできません。isSyncA()とisSyncB()は同じオブジェクト(オブジェクトx)にアクセスする同期ロックです。 
 

 // LockTest1.java   
 class Something {
  public synchronized void isSyncA(){
   try { 
    for (int i = 0; i < 5; i++) {
     Thread.sleep(); //   ms
     System.out.println(Thread.currentThread().getName()+" : isSyncA");
    }
   }catch (InterruptedException ie) { 
   } 
  }
  public synchronized void isSyncB(){
   try { 
   for (int i = 0; i < 5; i++) {
     Thread.sleep(100); //   100ms
     System.out.println(Thread.currentThread().getName()+" : isSyncB");
    }
   }catch (InterruptedException ie) { 
   } 
  }
 }
 public class LockTest {
  Something x = new Something();
  Something y = new Something(); 
  //   (01) x.isSyncA() x.isSyncB() 
  private void test1() {
  //   t11, t11    x.isSyncA()
   Thread t11 = new Thread(
     new Runnable() {
      @Override
      public void run() {
       x.isSyncA();
      }
    }, "t11");
   //   t12, t12    x.isSyncB()
  Thread t12 = new Thread(
    new Runnable() {
     @Override
     public void run() {
       x.isSyncB();
      }
    }, "t12"); 
  t11.start(); //   t11
   t12.start(); //   t12
 } 
  public static void main(String[] args) {
  LockTest1 demo = new LockTest1();
  demo.test1();
  }
 }
実行結果:
 
t 11:isSyncA
t 11:isSyncA
t 11:isSyncA
t 11:isSyncA
t 11:isSyncA
t 12:isSyncB
t 12:isSyncB
t 12:isSyncB
t 12:isSyncB
t 12:isSyncB
(02)同時にアクセスできます。アクセスは同じオブジェクトの同期ロックではないので、x.isSyncA()はxの同期ロックにアクセスし、y.isSyncA()はyの同期ロックにアクセスします。 

// LockTest2.java   
 class Something {
   public synchronized void isSyncA(){
     try { 
      for (int i = 0; i < 5; i++) {
         Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : isSyncA");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public synchronized void isSyncB(){
     try { 
       for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : isSyncB");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncA(){
     try { 
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncA");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncB(){
     try { 
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncB");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
 }
 public class LockTest2 {
   Something x = new Something();
   Something y = new Something();
  //   (02) x.isSyncA() y.isSyncA()
  private void test2() {
     //   t21, t21    x.isSyncA()
    Thread t21 = new Thread(
        new Runnable() {
           @Override
          public void run() {
           x.isSyncA();
          }
       }, "t21");
    //   t22, t22    x.isSyncB()
    Thread t22 = new Thread(
        new Runnable() {
          @Override
          public void run() {
             y.isSyncA();
          }
        }, "t22"); 
    t21.start(); //   t21
     t22.start(); //   t22
  }
   public static void main(String[] args) {
     LockTest2 demo = new LockTest2();
     demo.test2();
   }
 }
実行結果:
t 21:isSyncA
t 22:isSyncA
t 21:isSyncA
t 22:isSyncA
t 21:isSyncA
t 22:isSyncA
t 21:isSyncA
t 22:isSyncA
t 21:isSyncA
t 22:isSyncA 
(03)同時訪問はできません。cSyncA()とcSyncB()はいずれもstaticタイプなので、x.cyncA()はSomething.isSyncA()に相当し、y.SyncB()はSomethingn.isSyncB()に相当するので、同期ロックを共用しています。同時に問い返すことはできません。

 // LockTest3.java   
 class Something {
   public synchronized void isSyncA(){
     try { 
      for (int i = 0; i < 5; i++) {
         Thread.sleep(); //   ms
         System.out.println(Thread.currentThread().getName()+" : isSyncA");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public synchronized void isSyncB(){
     try { 
       for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : isSyncB");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncA(){
     try { 
       for (int i = 0; i < 5; i++) {
       Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncA");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncB(){
     try { 
       for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncB");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
 }
 public class LockTest3 {
   Something x = new Something();
   Something y = new Something();
  //   (03) x.cSyncA() y.cSyncB()
   private void test3() {
     //   t31, t31    x.isSyncA()
     Thread t31 = new Thread(
         new Runnable() {
           @Override
           public void run() {
             x.cSyncA();
           }
         }, "t31");
    //   t32, t32    x.isSyncB()
    Thread t32 = new Thread(
         new Runnable() {
           @Override
           public void run() {
             y.cSyncB();
           }
        }, "t32"); 
    t31.start(); //   t31
     t32.start(); //   t32
  } 
   public static void main(String[] args) {
    LockTest3 demo = new LockTest3();
    demo.test3();
   }
 }
実行結果:
t 31:cSyncA
t 31:cSyncA
t 31:cSyncA
t 31:cSyncA
t 31:cSyncA
t 32:cSyncB
t 32:cSyncB
t 32:cSyncB
t 32:cSyncB
t 32:cSyncB 
(04)同時アクセスが可能です。isSyncA()は例示的な方法であるため、x.isSyncA()はオブジェクトxのロックを使用している。cSyncA()は静的方法であり、Something.cyncA()は「クラスのロック」を使用することを理解できる。したがって、それらは同時に訪問することができる。 

// LockTest4.java   
 class Something {
   public synchronized void isSyncA(){
     try { 
       for (int i = 0; i < 5; i++) {
         Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : isSyncA");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public synchronized void isSyncB(){
     try { 
       for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : isSyncB");
       }
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncA(){
     try { 
      for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncA");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
   public static synchronized void cSyncB(){
     try { 
       for (int i = 0; i < 5; i++) {
        Thread.sleep(100); //   100ms
         System.out.println(Thread.currentThread().getName()+" : cSyncB");
       } 
     }catch (InterruptedException ie) { 
     } 
   }
 }
 public class LockTest {
   Something x = new Something();
   Something y = new Something();
  //   (04) x.isSyncA() Something.cSyncA()
  private void test4() {
     //   t41, t41    x.isSyncA()
     Thread t = new Thread(
         new Runnable() {
           @Override
           public void run() {
             x.isSyncA();
           }
        }, "t41");
     //   t42, t42    x.isSyncB()
    Thread t42 = new Thread(
         new Runnable() {
           @Override
          public void run() {
            Something.cSyncA();
          }
         }, "t42"); 
    t41.start(); //   t41
   t42.start(); //   t42
  } 
  public static void main(String[] args) {
    LockTest4 demo = new LockTest4();
    demo.test4();
  }
 }
実行結果: 
t 41:isSyncA
t 42:cSyncA
t 41:isSyncA
t 42:cSyncA
t 41:isSyncA
t 42:cSyncA
t 41:isSyncA
t 42:cSyncA
t 41:isSyncA
t 42:cSyncA
以上は小编が绍介したJava synchronizedのキーです。皆さんに何かお聞きしたいことがあれば、メッセージをください。小编はすぐにご返事します。ここでも私たちのサイトを応援してくれてありがとうございます。