1. 程式人生 > >Java多執行緒中避免在多生產者和多消費者場景中出現假死

Java多執行緒中避免在多生產者和多消費者場景中出現假死

在多執行緒程式設計中,如果所有執行緒全部都經由wait()方法進入等待狀態,那麼程式就進入了假死狀態

程式示例

考慮這個例子,來自《Java多執行緒程式設計核心技術》:

生產者類P:

//生產者
public class P {

	private String lock;

	public P(String lock) {
		super();
		this.lock = lock;
	}

	public void setValue() {
		try {
			synchronized (lock) {
				while (!ValueObject.
value.equals("")) { System.out.println("生產者 " + Thread.currentThread().getName() + " WAITING了★"); lock.wait(); } System.out.println("生產者 " + Thread.currentThread().getName() + " RUNNABLE了"); String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value; lock.notify(); } } catch (InterruptedException e) { e.printStackTrace(); } } }

消費者類C

//消費者
public class C {

	private String lock;

	public C(String lock) {
		super();
		this.lock = lock;
	}

	public void getValue() {
		try {
			synchronized (lock) {
while (ValueObject.value.equals("")) { System.out.println("消費者 " + Thread.currentThread().getName() + " WAITING了☆"); lock.wait(); } System.out.println("消費者 " + Thread.currentThread().getName() + " RUNNABLE了"); ValueObject.value = ""; lock.notify(); } } catch (InterruptedException e) { e.printStackTrace(); } } }

ValueObject類:

public class ValueObject {

	public static String value = "";

}

作為主類的Run類:

public class Run {

	public static void main(String[] args) throws InterruptedException {

		String lock = "";
		P p = new P(lock);
		C r = new C(lock);

		Thread[] pThread = new Thread[2];
		Thread[] rThread = new Thread[2];

		for (int i = 0; i < 2; i++) {
			pThread[i] = new Thread(()->{
				while(true){
					p.setValue();
				}
			});
			pThread[i].setName("生產者" + (i + 1));

			rThread[i] = new Thread(()->{
				while(true){
					r.getValue();
				}
			});
			rThread[i].setName("消費者" + (i + 1));

			pThread[i].start();
			rThread[i].start();
		}

		Thread.sleep(5000);
		Thread[] threadArray = new Thread[Thread.currentThread()
				.getThreadGroup().activeCount()];
		Thread.currentThread().getThreadGroup().enumerate(threadArray);

		for (int i = 0; i < threadArray.length; i++) {
			System.out.println(threadArray[i].getName() + " "
					+ threadArray[i].getState());
		}
	}

}

執行程式可能出現的一種情況是:

生產者 生產者1 RUNNABLE了
生產者 生產者1 WAITING了★
生產者 生產者2 WAITING了★
消費者 消費者2 RUNNABLE了
消費者 消費者2 WAITING了☆
消費者 消費者1 WAITING了☆
生產者 生產者1 RUNNABLE了
生產者 生產者1 WAITING了★
生產者 生產者2 WAITING了★
main RUNNABLE
生產者1 WAITING
消費者1 WAITING
生產者2 WAITING
消費者2 WAITING

可以看到,最後所有執行緒都處於等待狀態,程式最後也就呈現了“假死”狀態,不能夠繼續執行下去。

儘管已經使用了notify/wait進行執行緒之間的通訊,但是仍然出現假死現象,究其原因是因為連續喚醒同類執行緒

在多生產者多消費者的場景中,產生了“生產者”喚醒“生產者”或者“消費者”喚醒“消費者”的現象。如果這樣的情況越來越多,最終就會導致假死。

解決方法

解決假死問題可以通過簡單的使用notifyAll()方法的呼叫來解決即可。

這樣就可以有效確保每次喚醒的執行緒並非只有同類執行緒。