Java スレッドの排他制御(synchronized)

Javaのスレッドの排他制御のサンプルです。synchronizedを使用します。

目次

サンプル メソッドにsynchronizedを指定
  ブロックにsynchronizedを指定

メソッドにsynchronizedを指定

2つのスレッドが、1つのインスタンスの変数にほぼ同じタイミングでアクセスします。

このコードではsynchronizedで排他制御をかけていない場合以下の挙動になります。

想定:1つめのスレッドが変数の値の10に1を追加して11にして出力し、2つめのスレッドが同じ変数の値の11に1を追加し12にして出力する。

実際:それぞれのスレッドが同じ11を出力するときがある。

package test1;

public class Test1 {
	public static void main(String[] args) {
		Counter Counter = new Counter();
		Player player1 = new Player(Counter); // 同じインスタンスが引数
		Player player2 = new Player(Counter); // 同じインスタンスが引数
		new Thread(player1).start();
		new Thread(player2).start();
	}
}
class Counter {
	public int count = 10;
	//	public synchronized int plusOne() {
	public int plusOne() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.count = this.count + 1;
		return this.count;
	}
}
class Player implements Runnable {
	private Counter counter;
	public Player(Counter counter) {
		this.counter = counter;
	}
	@Override
	public void run() {
		int num = this.counter.plusOne();
		System.out.println(Thread.currentThread().getName()
				+ "は" + num + "件です");
	}
}

6,7行目のコンストラクタの引数は同じインスタンスです。
8,9行目は、それぞれスレッドが動きます。

21行目は、変数のcountに1を追加します。

実行するとまれに両方のスレッドの結果が11になります。

 

排他制御を行うためメソッドの前にsynchronizedを指定します。
15行目をコメントにして14行目のコメントを外すと実行結果は当初の想定どうりになります。

 

ブロックにsynchronizedを指定

上記コードではメソッドの前にsynchronizedを指定しましたが、代わりにsynchronizedとブロックでも排他制御をかけることができます。

class Counter {
	public int count = 10;
	// public synchronized int plusOne() {
	public int plusOne() {
		synchronized (this) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.count = this.count + 1;
			return this.count;
		}
	}
}

5~13行目が1つのブロックです。

関連の記事

Java マルチスレッドのサンプル(RunnableとThread)

△上に戻る