スレッドセーフではないコード(4回目)

今回はsynchronizedメソッドの話。「synchronizedが付いているメソッドは同時に実行されない」と思っていたら大まちがい。

下記に4種類のコード例のうち、System.out.println()が排他的に実行されるのは2つだけ。
System.outする箇所にブレークポイントを設定してデバッグすると動作がよく分かる。

SynchronizedTest.java

public class SynchronizedTest {
	public static void main(String[] args) {
		SomeThread someThread = new SomeThread();
		Thread t1 = new Thread(someThread);
		t1.setName("Thread-1");
		Thread t2 = new Thread(someThread);
		t2.setName("Thread-2");
		t1.start();
		t2.start();
	}
}

class SomeThread implements Runnable {
	public void run() {
		for (int i = 0; i < 100; i++) {
			// これは意図したとおり排他的に動く
			// mainの中でThreadをnewする時、同じSomeThreadのインスタンスを使っているので、
			// 同じロックでガードされる
			synchronizedMethod(i);
		}
	}

	private synchronized void synchronizedMethod(int i) {
		System.out.println(Thread.currentThread().getName() + " : " + i);
	}
}

SynchronizedTest2.java

public class SynchronizedTest2 {
	public static void main(String[] args) {
		SomeThread2 someThread = new SomeThread2();
		Thread t1 = new Thread(someThread);
		t1.setName("Thread-1");
		Thread t2 = new Thread(someThread);
		t2.setName("Thread-2");
		t1.start();
		t2.start();
	}
}

class SomeThread2 implements Runnable {
	public void run() {
		for (int i = 0; i < 100; i++) {
			// これは意図したとおり排他的に動かない
			// synchronizedMethod(i)はsynchronizedだが、SynchronizedMethodClassのインスタンスが
			// 異なる(毎回newしている)ので、ロックでガードされない
			new SynchronizedMethodClass().synchronizedMethod(i);
		}
	}
}

class SynchronizedMethodClass {
	synchronized void synchronizedMethod(int i) {
		System.out.println(Thread.currentThread().getName() + " : " + i);
	}
}

SynchronizedTest3.java

public class SynchronizedTest3 {
	public static void main(String[] args) {
		SomeThread3 someThread = new SomeThread3();
		Thread t1 = new Thread(someThread);
		t1.setName("Thread-1");
		Thread t2 = new Thread(someThread);
		t2.setName("Thread-2");
		t1.start();
		t2.start();
	}
}

class SomeThread3 implements Runnable {
	SynchronizedMethodClass3 smc = new SynchronizedMethodClass3();
	public void run() {
		for (int i = 0; i < 100; i++) {
			// これは意図したとおり排他的に動く
			// mainの中でThreadをnewする時、同じSomeThread3のインスタンスを使っているので、
			// 同じロックでガードされる
			smc.synchronizedMethod(i);
		}
	}
}

class SynchronizedMethodClass3 {
	synchronized void synchronizedMethod(int i) {
		System.out.println(Thread.currentThread().getName() + " : " + i);
	}
}

SynchronizedTest4.java

public class SynchronizedTest4 {
	public static void main(String[] args) {
		SomeThread4 someThread = new SomeThread4();
		SomeThread4 someThread2 = new SomeThread4();
		Thread t1 = new Thread(someThread);
		t1.setName("Thread-1");
		Thread t2 = new Thread(someThread2);
		t2.setName("Thread-2");
		t1.start();
		t2.start();
	}
}

class SomeThread4 implements Runnable {
	SynchronizedMethodClass4 smc = new SynchronizedMethodClass4();
	public void run() {
		for (int i = 0; i < 100; i++) {
			// これは意図したとおり排他的に動かない
			// mainの中でThreadをnewする時、違うSomeThread4のインスタンスを使っているので、
			// 同じロックでガードされない
			smc.synchronizedMethod(i);
		}
	}
}

class SynchronizedMethodClass4 {
	synchronized void synchronizedMethod(int i) {
		System.out.println(Thread.currentThread().getName() + " : " + i);
	}
}