デッドロックが発生しやすいスレッドコラボレーション

3804 ワード

プログラムが行う機能は、生産者と消費者に似ていることです.コードは次のとおりです.
public class AddEggThread implements Runnable {

	private Plate plate;
	private Object egg = new Object();
	public AddEggThread(Plate plate) {
		this.plate = plate;
	}
	
	@Override
	public void run() {
		plate.addEgg(egg);
	}
	
}

 
public class GetEggThread implements Runnable {

	private Plate plate;
	public GetEggThread(Plate plate) {
		this.plate = plate;
	}
	@Override
	public void run() {
		plate.getEgg();
	}

}

 
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Plate {

	private List<Object> eggs = new ArrayList<Object>();
	
	public  synchronized Object getEgg() {
		while(eggs.size() == 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Object egg = eggs.get(0);
		eggs.clear(); // 
		notify(); //  
		System.out.println(" ");
		return egg;
	}
	
	public synchronized void addEgg(Object egg) {
		while (eggs.size() > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		eggs.add(egg);
		notify();
		System.out.println(" ");
	}

	public static void main(String[] args) {
		Plate plate = new Plate();
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			exec.execute(new GetEggThread(plate));
			exec.execute(new AddEggThread(plate));
		}
		exec.shutdown();
	}
}

プログラムを実行すると、たまにデッドロックが発生します.家に帰った後、本の上で問題を解決する方法を見つけた.コードを先に入力:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Plate {

	private List<Object> eggs = new ArrayList<Object>();
	
	public  synchronized Object getEgg() {
		while(eggs.size() == 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		Object egg = eggs.get(0);
		eggs.clear(); // 
		notifyAll(); //  
		System.out.println(" ");
		return egg;
	}
	
	public synchronized void addEgg(Object egg) {
		while (eggs.size() > 0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		eggs.add(egg);
		notifyAll();
		System.out.println(" ");
	}

	public static void main(String[] args) {
		Plate plate = new Plate();
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			exec.execute(new GetEggThread(plate));
			exec.execute(new AddEggThread(plate));
		}
		exec.shutdown();
	}
}

2つのコードの違いを真剣に比較すると、notify()がnotifyAll()に変更されていることがわかります.なぜnotifyを使用するとデッドロックになるのですか?次のように解釈できます.
notify()は待機中のスレッドを1つだけ起動し、起動されたスレッドが現在解放されているロックを取得できるとは限らない.
notifyAll()ではなくnotify()を使用するのは最適化です.notifyを使用すると、待機しているタスクのうち1つだけが呼び出されます.したがってnotify()を使用するには、起動が適切なタスクであることを保証する必要があります.
実際のプログラムでは,notify()を用いて適切なタスクのみを起動することは困難である(2つのスレッドのみの場合を除く).そのため、より多くの場合notifyAll()を使用します.notifyAll()が「待っているすべてのタスク」を呼び覚ますという言い方は正しくありません.現在のロックを待つスレッドのみを起動します.