Javaコンカレントベース03.従来のスレッド反発テクノロジー-synchronized

4646 ワード

複数のスレッドで同じリソースを同時に操作すると、銀行振替や切符販売システムなど、同時的な問題に直面します.これらの問題を回避するために、synchronizedキーワードを使用して解決することができます.次に、synchronizedの一般的な使い方についてまとめます.まず、次のように、同時性の問題があるプログラムを書きます.
public class TraditionalThreadSynchronized {

	public static void main(String[] args) {
		// new 
		//private Outputer outputer = new Outputer();
		new TraditionalThreadSynchronized().init();
	}
	
	private void init() {
		final Outputer outputer = new Outputer();
		// 1 :duoxiancheng
		new Thread(new Runnable() {			
			@Override
			public void run() {
				while(true) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					outputer.output1("duoxiancheng");
				}
				
			}
		}).start();;
		
		// 2 :eson15
		new Thread(new Runnable() {			
			@Override
			public void run() {
				while(true) {
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					outputer.output1("eson15");
				}
				
			}
		}).start();;
	}
	
	static class Outputer {
		// , 
		public void output1(String name) {
			int len = name.length();
			for(int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println("");		
		}		
	}
}

内部クラスOutputerで文字列を印刷する方法を定義し、1文字1文字の印刷を行うと、文字の順序が狂っているため、同時問題が直感的に見られやすくなります.次にinitメソッドで2つのスレッドを開き、1つのスレッドが「duoxiancheng」を印刷し、もう1つのスレッドが「eson 15」を印刷します.運転結果を見てみましょう.
eson15duoxianche ng eson15 duoxiancheng duoxiancheng eson15 esduoxiancheng on15 duoxiancheng
問題が発生しました.この問題を解決するためにsynchronized同期コードブロックを使用して共通部分を同期操作することができますが、ロックを与える必要があります.このロックはオブジェクトであり、任意のオブジェクトであってもよいですが、2つのスレッドが同じオブジェクトロックを使用しなければならないことを前提としています.これはよく理解できます.では、output1()メソッドにsynchronizedコードブロックを追加します.
static class Outputer {
	private String token = ""; // 
	public void output1(String name) {
		synchronized(token) // , 
		// name , name , name
		{
			int len = name.length();
			for(int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println("");		
		}
	}
}	

上記の改造により、スレッドセキュリティの問題は基本的に解決されましたが、synchronizedキーワードをメソッドに追加すれば、この同期ロックは何ですか?Outputerクラスにoutput2()メソッドを書きます.
static class Outputer {
	private String token = ""; // 
	public void output1(String name) {
		synchronized(token) // , 
		{
			int len = name.length();
			for(int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println("");		
		}
	}	
	
	public synchronized void output2(String name) {
		
		int len = name.length();
		for(int i = 0; i < len; i++) {
			System.out.print(name.charAt(i));
		}
		System.out.println("");		
	}	
}

メソッドの内部実装ロジックはそっくりで、唯一異なるのはsynchronizedがメソッドに加算されていることであり、init()メソッドの2つのスレッドのうち、1つはoutput1(String name)メソッドを呼び出し、もう1つはoutput2(String name)メソッドを呼び出し、結果からスレッドセキュリティの問題がまた発生したことがわかる.問題の原因は容易に発見できない:現在2つの方法にsynchronizedが追加されているが、2つのスレッドが2つの異なる方法を呼び出しているのはやはり問題が発生している.つまり、やはりそれぞれ遊んでいるのか......では、問題はこのロックにあり、両者が同じロックを使用していないことを示している.output1()メソッドのsynchronizedのtokenをthisに変更して実行すれば問題ありません.synchronizedキーワード修飾メソッドの場合、同期ロックはthis、すなわちコードブロックsynchronized(this) {...}に等しいことを示します.さらに続く説明では、現在Outputerクラスに静的メソッドoutput3(String name)をもう1つ書き、synchronizedにこの静的メソッドを修飾させる.
static class Outputer {
	private String token = ""; // 
	public void output1(String name) {
		synchronized(token) // , 
		{
			int len = name.length();
			for(int i = 0; i < len; i++) {
				System.out.print(name.charAt(i));
			}
			System.out.println("");		
		}
	}	
	
	public static synchronized void output3(String name) {
		
		int len = name.length();
		for(int i = 0; i < len; i++) {
			System.out.print(name.charAt(i));
		}
		System.out.println("");		
		}	
	}
}

次いで、init()メソッドの1つのスレッドがoutput1()メソッドを呼び出し、もう1つのスレッドがoutput3()メソッドを呼び出す.スレッドセキュリティの問題がまた発生することは間違いありません.でもどうやって解決しますか?staticメソッドはクラスのロード時にロードされるので、このロックはクラスのバイトコードオブジェクトであるべきです.ではtokenをOutputer.classに変更すると問題が解決する.synchronizedキーワード修飾staticメソッドの場合、同期ロックはクラスのバイトコードオブジェクト、すなわちコードブロックsynchronized(classname.class) {...}に等しい.最後にまとめます.
  • 同期コードブロックのロックは任意のオブジェクトである.このロックは、異なるスレッドが同じ同期コードブロックを実行する場合に勝手に設定されます.
  • 同期関数のロックは固定されています.同期関数の論理と同期する必要がある場合、コードブロックのロックはthisでなければなりません.
  • 静的同期関数のロックは、その関数が属するクラスのバイトコードファイルオブジェクトである.このオブジェクトは、this.getClass()の方法で取得してもよいし、 .classで表されてもよい.