Javaマルチスレッド:Synchronizedキーワード同期クラスを使用する方法


本論文ではSynchronizedキーワード同期クラスを使用する方法を紹介します。Javaマルチスレッドのrunメソッド同期を達成するには、voidとpublicの間にsynchronizedキーワードを追加する必要があります。
 
汚いデータの問題を解決するためには、synchronizedキーを使ってrun方法を同期させるのが一番簡単です。コードは以下の通りです。
public synchronized void method() {
		}
  上記のコードから、voidとpublicの間にsynchronizedキーワードを加えれば、run方法を同期させることができます。つまり、同じJava類のオブジェクト例に対して、run方法は同時に一つのスレッドにのみ呼び出され、現在のrunが実行された後、他のスレッドに呼び出されることができます。現在スレッドがrunメソッドのyieldメソッドに実行されたとしても、一時停止しただけです。他のスレッドはrun方法を実行できないため、最終的には現在のスレッドによって実行されます。まず次のコードを見てください。
sychronizedキーは一つのオブジェクトインスタンスとのみ紐付けされます。
package com.jelly.thread;
/**
 * 
 * @author Jelly
 *
 */
public class Sync implements Runnable {
	private Test test;

	public Sync(Test test) {
		this.test = test;
	}

	public static void main(String[] args) {
		Sync sy1 = new Sync(new Test("t1"));
		Sync sy2 = new Sync(new Test("t2"));
		Thread th1 = new Thread(sy1);
		Thread th2 = new Thread(sy2);
		th1.start();
		th2.start();
	}

	public void run() {
		test.method();
	}
}

class Test {
	public String name;

	public Test(String name) {
		this.name = name;
	}

	public synchronized void method() {
		for (int i = 0; i < 100; i++) {
			System.out.println(name+i);
		}
	}
}
 
Testクラスでのmethod法は同期している。しかし、上のコードは2つのTestクラスの例を確立しており、したがって、test 1とtest 2のmethod方法はそれぞれ実行される。methodを同期させるには、Syncクラスのインスタンスを確立する際に、同じTestクラスのインスタンスがその構造方法に導入される必要があります。以下のコードで示します。
Sync sync 1=new Sync(test 1)    
 synchronizedを用いて非静的方法を同期させるだけでなく、synchronizedを用いて静的方法を同期させることもできる。以下のようにmethod方法を定義することができる:
class Test {
	public static synchronized void method() {
			}
}
 

建立Test类的对象实例如下:


 
  1. Test test =  new  Test(); 
静的方法では、synchronizedキーワードを加えれば、この方法は同期であり、test.method()を使用するか、またはTest.method()を使用してmethod方法を呼び出すかに関わらず、methodは同期であり、非静的方法の複数の例の問題は存在しない。
23種類の設計モードにおけるシングルウェアモードは、従来の方法で設計されても、スレッドが不安定であり、以下のコードはスレッドが安全でないシングルモードである。
package com.jelly.thread;
/**
 * 
 * @author Jelly QQ:136179492
 *
 */
public class Singleton {
	private static Singleton sample;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (sample == null) {
			Thread.yield();//     Singleton           
			sample = new Singleton();
		}
		return sample;
	}

	public static void main(String[] args) {
		MThread thread[] = new MThread[10];
		for (int i = 0; i < thread.length; i++) {
			thread[i] = new MThread();
		}
		for (int i = 0; i < thread.length; i++) {
			thread[i].start();
		}
	}

}

class MThread extends Thread {
	public void run() {
		Singleton singleton = Singleton.getInstance();
		System.out.println(singleton.hashCode());
	}
}
 
上のコードでyieldメソッドを呼び出したのは、シングルモードのスレッドの安全性を表現するためであり、この行を削除すると、上の実装は依然としてスレッドが安全ではなく、出現の可能性はかなり小さい。
プログラムの実行結果は以下の通りです。
25358555 26399554 7051261 29855319 5383406
上記の運転結果は、異なる運転環境ですべて同じものがあるかもしれませんが、この5行の出力は全く同じではありません。この出力結果から,get Instance法によって得られたオブジェクトの例は5つであり,我々が期待するものではないことがわかった。これは、スレッドがThread.yield()を実行した後、CPUリソースを他のスレッドに渡したからです。スレッド間切り替え時にSingletonオブジェクトのインスタンスを作成するステートメントが実行されないため、これらのスレッドはifで判断されているため、5つのオブジェクトインスタンスを作成する場合が発生します。(4つまたは3つのオブジェクトの例が作成される可能性があります。これは、どれぐらいのスレッドがSingletonオブジェクトを作成する前にifによって判断されていますか?実行毎に結果が異なる場合があります。)
上のシングルモードをスレッドセキュリティにするには、get Instanch ronizedキーを加えればいいです。コードは以下の通りです。

 
  1. public   static   synchronized  Singleton getInstance() {   } 
もちろん、Singleton変数を定義する際にSingletonオブジェクトを作成するより簡単な方法があります。コードは以下の通りです。

 
  1. private   static   final  Singleton sample =  new  Singleton(); 
そして、get Instanceの方法でsampleを直接返してもいいです。この方法は簡単ですが、get Instanceの方法でSingletonオブジェクトを作成するのは柔軟です。読者は具体的なニーズに応じて、さまざまな方法でシングルモードを実現します。
synchronizedキーワードを使う場合、以下の4つの点に注意してください。
1.  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();
	}
}
 
2.  インターフェースメソッドを定義するときは、synchronizedキーは使用できません。
3.  構造方法は、synchronizedキーを使用することはできませんが、次のセクションで議論するsynchronizedブロックを使用して同期することができます。
4.  synchronizedは自由に置くことができます。
前述の例では、いずれも方法の返却タイプの前にsynchronizedキーワードを置いていますが、これはsynchronizedではなく、唯一の位置に置くことができます。非静的な方法では、synchronizedはまた、方法定義の先頭に置くことができます。静的な方法では、synchronizedはstaticの前に置くことができます。コードは以下の通りです。
public synchronized void method(); 
synchronized public void method(); 
public static synchronized void method(); 
public synchronized static void method();  
synchronized public static void method(); 
 
 

但要注意,synchronized不能放在方法返回类型的后面,如下面的代码是错误的:

public void synchronized method(); 
public static void synchronized method(); 
 
synchronizedキーワードは同期方法しか使えません。クラス変数を同期させるためには使えません。下のコードも間違っています。
public synchronized int n = 0; 
public static synchronized int n = 0; 
synchronizedキーワードを使った同期方法は最も安全な同期方式ですが、synchronizedキーワードを大量に使うと、不必要な資源消費と性能損失を引き起こします。表面から見て、synchronizedロックは一つの方法ですが、実際にはsynchronizedロックは一つのクラスです。つまり、非静的な方法method 1とmethod 2で定義されています。synchronizedについては、method 1が実行されない前に、method 2は実行できません。静的方法と非静的方法の場合は似ていますが、静的および非静的方法は互いに影響しません。以下のコードを見てください。
 
package com.jelly.thread;

import java.lang.reflect.InvocationTargetException;

/**
 * 
 * @author Jelly QQ:136179492
 * 
 */
public class MyThread1 extends Thread {
	public String methodName;

	public static void method(String s) {
		System.out.println(s);
		while (true)
			;
	}

	public synchronized void method1() {
		method("    method1  ");
	}

	public synchronized void method2() {
		method("    method2  ");
	}

	public static synchronized void method3() {
		method("   method3  ");
	}

	public static synchronized void method4() {
		method("   method4  ");
	}

	public void run() {
		try {
			getClass().getMethod(methodName).invoke(this);
		} catch (IllegalArgumentException e) {
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		MyThread1 myThread1 = new MyThread1();
		for (int i = 1; i <= 4; i++) {
			myThread1.methodName = "method" + String.valueOf(i);
			new Thread(myThread1).start();
			sleep(100);
		}
	}
}
 
運転結果は以下の通りです。
非静的なmethod 1方法静的なmethod 3方法
以上の運転結果から、method 2とmethod 4は、method 1とmethod 3が終了しない前に動作できないことが分かりました。したがって、クラスでsynchronizedキーワードを使用して非静的な方法を定義すると、これに影響を与えるという結論が得られます。静的な方法を定義すると、影響があります。クラスでは、synchronizedキーで定義されたすべての静的方法が、データテーブルのテーブルロックに似ています。記録を変更すると、システムはテーブル全体をロックします。このような同期方式を多く使うと、プログラムの性能が大幅に低下します。