java 多線程 線程之間的協作 notify()和notifyAll()

    使用notify()而不是notifyAll()是一種優化。使用notify()時,在衆多等待同一個鎖的任務中只有一個會被喚醒,因此如果你希望使用notify(),就必須保證被喚醒的是恰當的任務。另外,爲了使用notify(),所有任務必須等待相同的條件,因爲如果你有多個任務在等待不同的條件,那麼你就不會知道是否喚醒了恰當的任務。如果使用notify(),當條件發生變化時,必須只有一個任務能夠從中受益。最後,這些限制對所有可能存在的子類都必須總是起作用。如果這些規則中有任何一條不滿足,那麼你就必須使用notifyAll()而不是notify()。

   

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 在有關Java的線程機制的討論中,有一個令人困惑的描述:notify()將喚醒“所有正在等待的任務”。這是否意味着在程序
 * 中任何地方,任何處於wait()狀態中的任務都將被任何對notifyAll()的調用喚醒呢?在下面的示例中,與Task2相關的
 * 代碼說明了情況並非如此---事實上,當notifyAll()因某個特定鎖而被調用時,只有等待這個鎖的任務纔會被喚醒。
 * 
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */

class Blocker {
	private String name;

	public Blocker(String name) {
		this.name = name;
	}

	synchronized void waitingCall() {
		try {
			while (!Thread.interrupted()) {
				wait();
				System.out.println(Thread.currentThread() + " " + name);
			}
		} catch (InterruptedException e) {
			// OK to exit this way
			System.out.println("InterruptedException");
		}
	}

	synchronized void prod() {
		notify();
	}

	synchronized void prodAll() {
		notifyAll();
	}
}
/**
 * Task 有其自己的Blocker對象。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
class Task implements Runnable {
	static Blocker blocker = new Blocker("Task");

	@Override
	public void run() {
		blocker.waitingCall();
	}
}
/**
 * Task2 有其自己的Blocker對象。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
class Task2 implements Runnable {
	// A separate Blocker object;
	static Blocker blocker = new Blocker("Task2");

	@Override
	public void run() {
		blocker.waitingCall();
	}
}
/**
 * Task和Task2每個都有其自己的Blocker對象,因此每個Task對象都會在Task.blocker上阻塞,而每個Task2都會在
 * Taks2.blocker上阻塞。在main()中,java.util.Timer對象唄設置爲每4/10秒執行一次run()方法,而這個run()
 * 方法將經由“激勵”方法交替地在Task.blocker上調用notify()和notifyAll()。
 * 從輸出中你可以看到,即使存在Task2.blocker上阻塞的Task2對象,也沒有任何在Task.blocker上的notify()或
 * notifyAll()調用會導致Task2對象被喚醒。於此類似,在main()的結尾,調用了timer的cancel(),即使計時器被撤銷了,
 * 前5個任務也依然在運行,並仍舊在它們對Task.blocker.waitingCall()的調用中被阻塞。對Task2.blocker.prodAll()
 * 的調用所產生的輸出不包括任何在Task.blocker中的鎖上等待任務。
 * 如果你瀏覽Blocker中的prod()和prodAll(),就會發現這是有意義的。這些方法是synchronized的,這意味着它們將獲取自身
 * 的鎖,因此當它們調用notify()或notifyAll()時,只在這個鎖上調用是符合邏輯的---因此,將只喚醒在等待這個特定鎖的任務。
 * Blocker.waitingCall()非常簡單,以至於在本例中,你只需聲明for(;;)而不是while(!Thread.interrupted())就可以到達
 * 相同的效果,因爲在本例中,由於異常而離開循環和通過檢查interrupted()標誌離開循環時沒有任何區別的---在兩種情況下都要
 * 執行相同的代碼。但是事實上,這個示例選擇檢查interrupted(),因爲存在着兩種離開循環的方式。如果在以後的某個時刻,
 * 你決定要循環中添加更多的代碼,那麼如果沒有覆蓋從這個循環中退出的這兩條路徑,就會產生引入錯誤的風險。
 * 
 * @create @author Henry @date 2016-12-21
 * 
 */
public class NotifyVsNotifyAll {
	public static void main(String[] args) throws Exception {
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < 5; i++)
			exec.execute(new Task());
		exec.execute(new Task2());
		Timer timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			boolean prod = true;

			@Override
			public void run() {
				if (prod) {
					System.out.println("\nnotify()");
					Task.blocker.prod();
					prod = false;
				} else {
					System.out.println("\nnotifyAll()");
					Task.blocker.prodAll();
					prod = true;
				}
			}
		}, 400, 400);// Run every .4 second
		TimeUnit.SECONDS.sleep(5);
		timer.cancel();
		System.out.println("\n Timer canceled");

		TimeUnit.MILLISECONDS.sleep(500);
		System.out.println("Task2.blocker.prodAll()");
		Task2.blocker.prodAll();

		TimeUnit.MILLISECONDS.sleep(500);
		System.out.println("\n Shutting down");
		exec.shutdownNow();
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章