Synchronized()

7145 ワード

一、Synchronizedが使用する場所
同期の実装はもちろんロックを採用しており、javaでロックを使用する2つの基本ツールはsynchronizedとLockである.
ずっとsynchronizedが好きで、使いやすいからです.たとえば、メソッドを同期する必要がある場合は、メソッドの署名にsynchronizedキーワードを追加するだけです.
//未同期の方法
public void test() {}

1.1メソッドの署名の上に置く
//同期の方法
pubilc synchronized void test() {}

1.2コードブロックの上に置く
synchronizedはコードブロックにも使えます
public void test() {
     synchronized(obj) {
          System.out.println("===");
     }
}

1.3両方式の違い
synchronizedの使い方とコードブロックの違いは何ですか?
synchronizedはメソッド署名(testを例に)に使用され、あるスレッドがこのメソッドを呼び出すと、そのインスタンスのオブジェクトロックが取得され、メソッドが終了するまで他のスレッドは待つしかありません.このメソッドが実行されると、オブジェクトロックが解放されます.他のスレッドは、このロックをプリエンプトし、メソッドtestを実行する機会がありますが、すべてのスレッドが使用する同じオブジェクトインスタンスが発生してこそ、反発現象を実現することができます.そうでなければsynchronizedキーワードは意味を失います.
(ただし、このメソッドがクラスメソッド、すなわち修飾子がstaticである場合、synchronizedは、このメソッドを呼び出すスレッドが現在クラスのロックを持っていることを意味します.スレッドが現在のメソッド内で実行され続けている限り、他のスレッドはメソッドの使用権を得ることができません!)
synchronizedコードブロックでの使用方法:synchronized(obj){//todo code here}
スレッドがこのコードブロック内で実行されると、objオブジェクトのオブジェクトロックがあり、複数のスレッドが同じObjectオブジェクトを共有すると、反発が形成されます!特にobj==thisの場合、現在メソッドが呼び出されているインスタンスオブジェクトを表します.すなわち
public void test() {
     ...
     synchronized(this) {
          // todo your code
     }
     ...
}

 
この場合、その効果は
public synchronized void test() {
     // todo your code
}

synchronizedコードブロックを使用すると、同期が必要なコードのみを同期することができ、効率を大幅に向上させることができます.
まとめ:
synchronizedコードブロックを使用すると、方法には2つの利点があります.
1、同期が必要な場合のみ使用可能
2、wait()/notify()/nitifyAll()と一緒に使うと便利
1.4 wait()とnotify()/notifyAll()
この3つの方法はすべてObjectの方法で、スレッドの方法ではありません!
wait():占有されたオブジェクトロックを解放し、スレッドが待機プールに入り、cpuを解放し、他の待機中のスレッドがこのロックをプリエンプトし、ロックされたスレッドを取得すればプログラムを実行できます.sleep()とは異なり、スレッドがこのメソッドを呼び出すと、しばらく休眠し、休眠中にcpuは一時的に解放されますが、オブジェクトロックは解放されません.つまり、スリープ中も他のスレッドはこのコードの内部に入ることができません.スリープが終了し、スレッドはcpuを再取得し、コードを実行します.wait()とsleep()の最大の違いは、wait()がオブジェクトロックを解放し、sleep()が解放しないことです.
notify():この方法は、オブジェクトを呼び出すwait()によって待機しているスレッドを呼び覚ますが、実際にはオブジェクトロックを呼び覚ますことであり、wait()のスレッドがオブジェクトロックを取得する機会を与える.notify()を呼び出すと、すぐにロックが解除されるのではなく、synchronizedのコードがすべて実行されるまで、オブジェクトロックが解放されません.JVMは、待機中のスレッドにオブジェクトロックを取得するスレッドをスケジューリングし、コードを実行します.wait()およびnotify()はsynchronizedコードブロックで呼び出さなければならないことに注意してください.notifyAll()は、待機しているすべてのスレッドを起動します.
1.5 waitとnotifyの使用例
package com.yuan.test;
//   
public class Produce implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		int count = 10;
		while (count > 0) {
			synchronized (Test.obj) {//Test.obj       

				// System.out.print("count = " + count);
				System.out.print("A");
				count--;
				try {
					Thread.sleep(5000);//      sleep
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				/*
				 *   ,    :    notify wait         ,
				 *      notify   wait,
				 *        wait,        currentthread 。
                                 */
				try {
					Test.obj.notify();
					Test.obj.wait();
					
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}

	}
}
package com.yuan.test;
//   
public class Consumer implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
       int count=10;
       while(count>0){
    	    synchronized (Test. obj) {//Test.obj       
				System.out.println("B");
				count--;
				/*
				 *   ,    :    notify wait         ,
				 *      notify   wait,
				 *        wait,        currentthread 。
                                 */
				 try {
					 Test. obj.notify(); //       
                                         Test. obj.wait();
                    
                     
               } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                     e.printStackTrace();
               }
			}   
    	   
       }
	}

}
package com.yuan.test;
//   
public class Test {
	public static final Object obj = new Object();
    
    public static void main(String[] args) {
          
           new Thread( new Produce()).start();//      
           new Thread( new Consumer()).start();
          
    }
}

1.6ロックはnotify()とwait()の同じ機能を実現する
ReentrantLockはsynchronizedと同じ同時性とメモリの意味を持ち、割り込みロック待機とタイミングロック待機も含まれている.これは、スレッドAがオブジェクトobjのロックを先に取得した場合、スレッドBが所定時間待機してもロックを取得できない場合、自動的にロックを破棄することを意味する.
しかしsynchronizedはJVMレベルで実現するため、システムはロックの解放の有無を監視することができるが、ReentrantLockはコードで実現され、システムは自動的にロックを解放することができず、コードの中でfinally句の中でロックlockを明示的に解放する必要がある.unlock();
同じ例でlockを使ってどのように実現しますか?
package com.yuan.test;

import java.util.concurrent.locks.Lock;
//   
public class Produce implements Runnable {

	private Lock lock;
    public Produce(Lock lock) {
           this. lock = lock;
    }
    @Override
    public void run() {
           // TODO Auto-generated method stub
           int count = 10;
           while (count > 0) {
                try {
                    lock.lock();
                    count --;
                    System. out.print( "A");
               } finally {
                    lock.unlock();//  lock     
                     try {
                          Thread. sleep(90L);
                    } catch (InterruptedException e) {
                           // TODO Auto-generated catch block
                          e.printStackTrace();
                    }
               }
          }
    }
}
package com.yuan.test;
//   
import java.util.concurrent.locks.Lock;

public class Consumer implements Runnable {
/*
 *     synchronized  JVM     ,
 *               , ReentrantLock       ,
 *          ,
 *       finally        lock.unlock();
 * 
 */
	private Lock lock;
    public Consumer(Lock lock) {
           this. lock = lock;
    }
    @Override
    public void run() {
           // TODO Auto-generated method stub
           int count = 10;
           while( count > 0 ) {
                try {
                     lock.lock();
                     count --;
                    System. out.print( "B");
               } finally {
                     //lock.unlock(); 
                     try {
                          Thread. sleep(91L);
                    } catch (InterruptedException e) {
                           // TODO Auto-generated catch block
                          e.printStackTrace();
                    }
               }
          }

    }

}
package com.yuan.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//  
public class Test {
	private static final long serialVersionUID = 7373984872572414699L;
	
	public static void main(String[] args) {
		Lock lock = new ReentrantLock();//           ,                      

		Consumer consumer = new Consumer(lock);
		Produce producer = new Produce(lock);
		new Thread(consumer).start();
		new Thread(producer).start();
		
		
	}
}