Javaマルチスレッドのsynchronizedキーワードの概要2.0

5622 ワード

前回の結果を避けるためにJavaはsynchronizedキーワードを使用しています.
synchronizedキーワードの後のクラスのインスタンスを使用してロックされたコードを呼び出すには、このインスタンスが対応するロックを取得できる必要があります.1つのロックは同時に1つのインスタンスによってしか入手できません.
では、判断の根本的な根拠は、インスタンスが取得する必要があるのは同じロックではないかということです.同じロックであれば、対応するコードは同時に実行できません.同じロックでなければ、対応するコードは同時に実行できます.
彼には4つの状況があります.
1つ目--->コードブロックオブジェクトロック:
public class Test implements Runnable {

	static Test instance = new Test();
	
	Object lock = new Object();

	public static void main(String[] args) {
		Thread thread1 = new Thread(instance);
		Thread thread2 = new Thread(instance);

		thread1.start();
		thread2.start();
		while (thread1.isAlive() || thread2.isAlive()) {
		}
		L.p("Test main       ");

	}

	@Override
	public void run() {
		
		synchronized (lock){
			L.p("   " + Thread.currentThread().getName());
			try {
				Thread.sleep(3 * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			L.p(Thread.currentThread().getName() + "     ");
		}
		
	
	}
}

プログラム出力の結果は次のとおりです.
Thread-0と申します    //  1先に印刷して、3秒後2 3はほとんど同時に印刷して、3秒後、4 5はほとんど同時にThread-0を印刷して実行して終わります私はThread-1 Thread-1と言います実行して終わりますTest main関数は実行して終わります
比較すると、コードを次のように変更します.
public class Test implements Runnable {

	static Test instance1 = new Test();
	static Test instance2 = new Test();
	
	Object lock = new Object();

	public static void main(String[] args) {
		Thread thread1 = new Thread(instance1);
		Thread thread2 = new Thread(instance2);

		thread1.start();
		thread2.start();
		while (thread1.isAlive() || thread2.isAlive()) {
		}
		L.p("Test main       ");

	}

	@Override
	public void run() {
		synchronized (lock){
			L.p("   " + Thread.currentThread().getName());
			try {
				Thread.sleep(3 * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			L.p(Thread.currentThread().getName() + "     ");
		}
	}
}

今回は同じインスタンスではなく、2つの異なるインスタンスになりました.プログラムの出力は次のようになります.
Thread-1と申します  //1 2ほぼ同時印刷、3秒後3 4 5ほぼ同時印刷私の名前はThread-0 Thread-1実行終了Thread-0実行終了Test main関数実行完了
どうしてこんなことになったの?我々の判断によれば、instance 1とinstance 2には2つの異なるロックが必要であるため、互いに影響を及ぼさずに同時に実行することができる.
前回の変更に基づいて変更します  「Object lock=new Object();」を  “static Object lock = new Object();”
プログラムは次のように出力されます.
Thread-0と申します    //  1先に印刷して、3秒後2 3はほとんど同時に印刷して、3秒後、4 5はほとんど同時にThread-0を印刷して実行して終わります私はThread-1 Thread-1と言います実行して終わりますTest main関数は実行して終わります
理由はやはり判断基準から出発する:instance 1とinstance 2は同じロックを取得する必要があり、これはstatic修飾のメンバーがすべてのオブジェクトに共有されているためである.すなわち、lockオブジェクトはinstance 1とinstance 2によって共有され、instance 1とinstance 2に必要なのはこのlockロックであるため、instance 1が実行されるとinstance 2は実行できず、instance 1が実行されるまでinstance 2が必要なロックを取得し、対応するコードを実行する必要がある.
2つ目--->一般的な方法ロック:
public class Test implements Runnable {

	static Test instance = new Test();
	

	public static void main(String[] args) {
		Thread thread1 = new Thread(instance);
		Thread thread2 = new Thread(instance);

		thread1.start();
		thread2.start();
		while (thread1.isAlive() || thread2.isAlive()) {
		}
		L.p("Test main       ");

	}

	@Override
	public void run() {
		method();
	}
	
	public synchronized void method() {
		L.p("   " + Thread.currentThread().getName());
		try {
			Thread.sleep(3 * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		L.p(Thread.currentThread().getName() + "     ");
	}
}

出力は次のとおりです(理由は後述しません):
Thread-0と申します    //  1先に印刷して、3秒後2 3はほとんど同時に印刷して、3秒後、4 5はほとんど同時にThread-0を印刷して実行して終わります私はThread-1 Thread-1と言います実行して終わりますTest main関数は実行して終わります
変更:
public class Test implements Runnable {

	static Test instance1 = new Test();
	static Test instance2 = new Test();
	

	public static void main(String[] args) {
		Thread thread1 = new Thread(instance1);
		Thread thread2 = new Thread(instance2);

		thread1.start();
		thread2.start();
		while (thread1.isAlive() || thread2.isAlive()) {
		}
		L.p("Test main       ");

	}

	@Override
	public void run() {
		method();
	}
	
	public synchronized void method() {
		L.p("   " + Thread.currentThread().getName());
		try {
			Thread.sleep(3 * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		L.p(Thread.currentThread().getName() + "     ");
	}
}

出力は次のとおりです(理由は後述しません):
Thread-1と申します  //1 2ほぼ同時印刷、3秒後3 4 5ほぼ同時印刷私の名前はThread-0 Thread-1実行終了Thread-0実行終了Test main関数実行完了
前回修正した上で修正する   「public synchronized void method()」を「public static synchronized void method()」に変更します(これは実際には第3の種類のロックに分けることができます...)
出力は次のとおりです(理由は後述しません):
Thread-0と申します    //  1先に印刷して、3秒後2 3はほとんど同時に印刷して、3秒後、4 5はほとんど同時にThread-0を印刷して実行して終わります私はThread-1 Thread-1と言います実行して終わりますTest main関数は実行して終わります
3つ目-->
public class Test implements Runnable {

	static Test instance1 = new Test();
	static Test instance2 = new Test();

	public static void main(String[] args) {
		Thread thread1 = new Thread(instance1);
		Thread thread2 = new Thread(instance2);

		thread1.start();
		thread2.start();
		while (thread1.isAlive() || thread2.isAlive()) {
		}
		L.p("Test main       ");

	}

	@Override
	public void run() {
		synchronized (Test.class) {
			L.p("   " + Thread.currentThread().getName());
			try {
				Thread.sleep(3 * 1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			L.p(Thread.currentThread().getName() + "     ");
		}
	}
}

出力は次のとおりです(理由は後述しません):
Thread-0と申します    //  1先に印刷して、3秒後2 3はほとんど同時に印刷して、3秒後、4 5はほとんど同時にThread-0を印刷して実行して終わります私はThread-1 Thread-1と言います実行して終わりますTest main関数は実行して終わります