インスタンスJavaのsynchronizedキーワードとスレッドセキュリティの問題の解析

7143 ワード

まずsynchronizedの基本的な使用を振り返ってみましょう.
  • synchronizedコードブロックは、修飾されたコードが同期文ブロックとなり、その役割の範囲はこのコードブロックを呼び出すオブジェクトであり、synchronizedキーワードを使用する場合、コードセグメントを縮小できる範囲はできるだけ縮小され、コードセグメントに同期を加えることができ、これ以上方法全体に同期を加える必要はありません.これは、ロックの粒度を小さくし、コードをより大きく同時化することと呼ばれます.
  • synchronizedメソッドは、修飾されたメソッドが同期メソッドとなり、その作用範囲はメソッド全体であり、作用オブジェクトはこのメソッドを呼び出すオブジェクトである.
  • synchronized静的メソッドは、static静的メソッドを修飾し、その作用範囲は静的メソッド全体であり、作用オブジェクトはこのクラスのすべてのオブジェクトである.
  • synchronizedクラスは、Synchronizedの後ろに括弧で囲まれた部分synchronized(className.class)であり、作用するオブジェクトはこのクラスのすべてのオブジェクトである.
  • synchronized()ではロックされたオブジェクトであり、synchronized(this)がロックされているのはオブジェクト自体であり、同じクラスの異なるオブジェクト呼び出しのsynchronizedメソッドはロックされないが、synchronized(className.class)はグローバルロックの機能を実現し、このクラスのすべてのオブジェクト呼び出しはロックの影響を受け、さらに()には、特定のオブジェクトを追加して、特定のオブジェクトにロックをかけることもできます.
  • 
    synchronized (object) {
     //               
    }
    

    synchronizedキーワードとスレッドセキュリティsynchronizedキーワードでコードを包むとスレッドが同期してセキュリティになると思います.テストしました.完全に間違っていることに気づいた.synchronizedは正しく使用する必要があります.本当のスレッドは安全です..この書き方を知っていながら、怠け者だと思って間違った方法を使ってしまった.基礎がまだできていないようだ.復習を強化する必要があります!仕事中にこのような間違いを犯すのは許せない.synchronizedキーワードを使う場所はデータに敏感であることを知っておく必要がある.汗びっしょり...先にコードを貼り付けます.
    
    package com; 
     
    public class ThreadTest { 
     public static void main(String[] args) { 
      MyThread m1 = new MyThread(1); 
      MyThread m2 = new MyThread(2); 
      m1.start(); 
      m2.start(); 
     } 
    } 
     
    final class MyThread extends Thread { 
     private int val; 
     
     public MyThread(int v) { 
      val = v; 
     } 
     //              
     public synchronized void print1(int v) { 
      for (int i = 0; i < 100; i++) { 
       System.out.print(v); 
      } 
     } 
     
     public void print2(int v) { 
      //     
      synchronized (MyThread.class) { 
       for (int i = 0; i < 100; i++) { 
        System.out.print(v); 
       } 
      } 
     } 
     
     public void run() { 
      print1(val); 
      // print2(val); 
     } 
    } 
    

    やはりサボるために、汗びっしょり...プログラマーはいつも怠け者だろう.書くことが少なければ書くことが少ない.私はMyThreadを匿名の最終的な内部クラスに書き、呼び出しやすいようにしました.スレッドクラスを実装するために最も直接的な継承Threadを使用し、実行するrun()メソッドを定義します.まずprint 2()メソッドにコメントし、print 1()の結果を見てみましょう.print 1()はsynchronizedキーワード定義を用いた方法で、これでスレッドセキュリティも実現できると思っていました.私が間違っていることを知らなかった.main()メソッドを直接実行します.コンソールの印刷結果は次のとおりです.
     
      
    1212111121212121212121212121212121212121222222212121212。。。

    一連の1と2がクロスプリントされた結果です.私のmainメソッドでは、m 1を先に実行してからm 2を実行し、スレッド同期ができていないことを示しています.
    
    MyThread m1 = new MyThread(1); 
    MyThread m2 = new MyThread(2); 
    m1.start(); 
    m2.start(); 
    

    次にrunメソッドのprint 1()を注釈し、print 2()を実行します.コンソールは次のように印刷されます.
     
      
    11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

    スレッドはやはり安全なので、この書き方も知っていると思っていたのですが、この書き方コードが少し多くてもあまり考えていなかったので、今日やっとこの間違いに気づきました.怠け者でないのはメリットがあるようです.基礎を築くことが大切だ.長い間の誤りを正す.
    具体的な原因を見てみましょう.
    synchronizedキーワードは、関数の修飾子としてもよいし、関数内の文としてもよい.すなわち、通常の同期方法と同期文ブロックとして用いられる.さらに詳細な分類の場合、synchronizedはinstance変数、object reference(オブジェクト参照)、static関数、class literals(クラス名字面定数)に作用します.さらに述べる前に、A.synchronizedキーワードがメソッドに追加されてもオブジェクトに追加されても、コードや関数をロックDとしてではなく、取得したロックはオブジェクトであり、同期メソッドは他のスレッドのオブジェクトにもアクセスされる可能性が高いことを明確にする必要があります.B.オブジェクトごとにロックが1つしかない(lock)これに関連する.C.同期を実現するには大きなシステムオーバーヘッドを対価とし、デッドロックを引き起こす可能性もあるので、無駄な同期制御をできるだけ避ける.次にsynchronizedがコードに与える影響を異なる場所で用いることを議論する:P 1,P 2が同じクラスの異なるオブジェクトであると仮定し、このクラスでは以下のような場合の同期ブロックまたは同期方法、P1、P 2はいずれも呼び出すことができる.1.synchronizedを関数修飾子とする場合、サンプルコードは以下の通りです.
    
    Public synchronized void methodAAA() 
    { 
    //…. 
    } 
    
    

    これが同期方法ですが、synchronizedがロックしているオブジェクトはどれですか?この同期メソッドオブジェクトを呼び出すことをロックします.すなわち、1つのオブジェクトP 1が異なるスレッドでこの同期方法を実行すると、それらの間に反発が形成され、同期の効果が得られる.しかし、このオブジェクトが属するClassによって生成された別のオブジェクトP 2は、synchronizedキーワードが付加されたメソッドを任意に呼び出すことができる.上記のサンプルコードは、次のコードと同じです.
    
    public void methodAAA() 
    { 
    synchronized (this) // (1) 
    { 
    //….. 
    } 
    } 
    

    (1)のthisは何を指していますか.このメソッドを呼び出すオブジェクト(P 1など)を指します.可視同期法は実質的にsynchronizedをobject referenceに作用させる.D�DそれはP 1オブジェクトのロックのスレッドを手に入れて、やっとP 1の同期方法を呼び出すことができて、P 2にとって、P 1このロックはそれと少しも関系がなくて、プログラムもこのような情况の下で同期のメカニズムの制御を抜け出して、データの混乱をもたらすかもしれません!
    2.ブロックを同期します.サンプルコードは次のとおりです.
    
    public void method3(SomeObject so) 
    { 
    synchronized(so) 
    { 
    //….. 
    } 
    } 

    この場合、ロックはsoというオブジェクトであり、誰がこのロックを手に入れると、その制御されたコードを実行することができます.ロックとして明確なオブジェクトがある場合は、このようにプログラムを書くことができますが、ロックとして明確なオブジェクトがなく、コードを同期させたいだけである場合は、ロックとして特殊なinstance変数(オブジェクトでなければなりません)を作成することができます.
    
    class Foo implements Runnable 
    { 
    private byte[] lock = new byte[0]; //    instance   
    Public void methodA() 
    { 
    synchronized(lock) { //… } 
    } 
    //….. 
    } 
    

    注意:長さゼロのbyte配列オブジェクトを作成すると、どのオブジェクトよりも経済的になります.Dコンパイルされたバイトコードを表示します.長さゼロのbyte[]オブジェクトを生成するには3つの操作コードしか必要ありません.Object lock=new Object()には7行の操作コードが必要です.3.static関数にsynchronizedを使用します.サンプルコードは次のとおりです.
    
    Class Foo 
    { 
    public synchronized static void methodAAA() //    static    
    { 
    //…. 
    } 
    public void methodBBB() 
    { 
    synchronized(Foo.class) // class literal(       ) 
    } 
    } 
    

    コード中のmethodBBB()メソッドはclass literalをロックとする場合であり,同期したstatic関数が生み出す効果と同じであり,得られるロックは特別である.このメソッドを現在呼び出すオブジェクトが属するクラス(Classは、このClassによって生成された特定のオブジェクトではない).Foo.classとP 1.getClass()を同期ロックに使用するのは、P 1が使用できないことを『Effective Java』で見たことがある.getClass()は、このClassをロックする目的を達成します.P 1とは、Fooクラスによって生成されるオブジェクトを指す.クラスにsynchronizedのstatic関数Aが定義され、synchronizedのinstance関数Bが定義されている場合、クラスの同じオブジェクトObjがマルチスレッドでAとBの2つのメソッドにそれぞれアクセスした場合、同期は構成されません.ロックが異なるためです.AメソッドのロックはObjというオブジェクトであり,BのロックはObjが属するクラスである.まとめは、synchronizedがロックしているオブジェクトを明らかにすることで、より安全なマルチスレッドプログラムの設計を支援します.共有リソースへの同期アクセスをより安全にするには、public/protectedのinstance変数を定義するのではなく、privateのinstance変数+そのgetメソッドを定義します.変数をpublicと定義すると、オブジェクトは外部で同期メソッドの制御を迂回して直接取得し、変更できます.これもJavaBeanの標準的な実装方法の一つです.2.instance変数が配列やArrayListなどのオブジェクトである場合、上記の方法は依然として安全ではありません.外部オブジェクトがgetメソッドでこのinstanceオブジェクトの参照を取得した後、別のオブジェクトに指向すると、このprivate変数も変わり、危険ではありません.このときgetメソッドにsynchronizedを加えて同期し,このprivateオブジェクトのclone()D,Dのみを返すように,呼び出し端で得られるのがオブジェクトコピーの参照である.
    synchronizedの注意事項をまとめます.
  • 同じオブジェクトのsynchronizedコードブロックに2つの同時スレッドがアクセスすると、同じ時点で1つのスレッドしか実行されず、別のスレッドがブロックされ、現在のスレッドがこのコードブロックを実行してからコードブロックを実行するまで待たなければならない.synchronizedコードブロックを実行すると現在のオブジェクトがロックされるため、2つのスレッド間で反発します.このオブジェクトロックは、コードブロックを実行してから解放され、次のスレッドが実行されてロックされます.
  • あるスレッドがobjectのsynchronized(this)同期コードブロックにアクセスすると、別のスレッドはobjectの非synchronized(this)同期コードブロックにアクセスすることができる.(2つのスレッドは同じオブジェクトを使用しています)
  • スレッドがobjectのsynchronized(this)同期コードブロックにアクセスすると、他のスレッドのobject内の他のすべてのsynchronized(this)同期コードブロックへのアクセスがブロックされます(同じように、2つのスレッドは同じオブジェクトを使用します).