這篇文章主要講解多線程編程中 wait與notify與notifyAll 這三個方法的運用,瞭解了它們的基本用法後,我們再寫個 “積累能量---放大招” 的例子來整合演示這幾個方法的協作運用;
見名之意,wait是等待的意思,當在線程A內調用wait()時,線程A將暫停運行,直到其它的線程通過調用notify或者notifyAll方法喚醒它爲止;
這三個方法都是從Object類繼承而來的,它們必須運行在持鎖的代碼中,否則將拋出異常,想一想,爲什麼這些方法會定義在Object中,又爲什麼必須運行在持鎖的情況下呢?答案提示:想想同步的意義...
wait與notifyAll的用法:
我們先下一個結論:wait是會釋放鎖的,而sleep是不會釋放鎖的!接下來我們來看代碼:
class TempObj{
//用來證明sleep是不會釋放鎖的
public synchronized void bySleep(){
System.out.println("tempObj.bySleep()");
try {
TimeUnit.MILLISECONDS.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//用來證明wait是會釋放鎖的
public synchronized void byWait(){
System.out.println("tempObj.byWait()");
//調用wait,使當前線程進入等待狀態
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void show(){
//正常輸出內容
System.out.println("tempObj.show()");
}
}
//演示sleep不會釋放鎖,而wait是會釋放鎖的
public static void sleepAndWait() throws InterruptedException{
final TempObj obj = new TempObj();
//線程T1
Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("run thread t1");
// obj.bySleep(); //代碼1處
obj.byWait();
System.out.println("run thread t1...over...");
}
});
//線程T2
Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println("run thread t2");
obj.show();
System.out.println("run thread t2...over");
}
});
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(t1);
TimeUnit.MILLISECONDS.sleep(10);
exec.execute(t2);
exec.shutdown();
}
輸出******************************************************************
run thread t1
tempObj.byWait()
run thread t2
tempObj.show()
run thread t2...over
**********************************************************************'
我們開啓了兩個線程T1以及T2,t1先運行,首先打印出【run thread t1】,
它在內部將調用TempObj的byWait方法,接着打印出【tempObj.byWait()】,
接下來在它內部調用了wait(),這將導致當前線程阻塞且釋放鎖,所以沒有打印出【run thread t1...over...】;
而T2線程調用了TempObj的show方法,此時T1已釋放了鎖,所以能打印出【tempObj.show()】
如果我們將obj.byWait()方法註釋,然後將Obj.bySleep()註釋取消,則結果又會是怎樣呢,我們來看看輸出:
輸出******************************************************************
run thread t1
tempObj.bySleep()
run thread t2
**********************************************************************'
public synchronized void show(){
//正常輸出內容
System.out.println("tempObj.show()");
//喚醒正在等待當前對象鎖的所有線程,T1線程正在等待此對象鎖
notifyAll();
}
tempObj.byWait()
run thread t2
tempObj.show()
run thread t2...over
run thread t1...over...
//演示notify,notifyAll 的喚醒順序
public static void notifyAndNotifyAll() throws InterruptedException{
class Temp{
public synchronized void show(){
System.out.println(Thread.currentThread().getName()+".temp.show()");
try {
wait();
} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+".temp.show()...over...");
}
}
final Temp temp = new Temp();
class MyThread extends Thread{
public MyThread( String name ) {
super.setName(name);
}
@Override
public void run() {
temp.show();
}
}
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
exec.execute(new MyThread("線程"+i));
}
TimeUnit.MILLISECONDS.sleep(1000);
//注意,wait以及notify等方法調用,必須是在持有其對象鎖的情況下!
synchronized (temp) {
temp.notify();
// temp.notifyAll();
}
exec.shutdown();
}
pool-1-thread-4.temp.show()
pool-1-thread-3.temp.show()
pool-1-thread-5.temp.show()
pool-1-thread-2.temp.show()
pool-1-thread-1.temp.show()...over...
pool-1-thread-3.temp.show()
pool-1-thread-1.temp.show()
pool-1-thread-5.temp.show()
pool-1-thread-4.temp.show()
pool-1-thread-4.temp.show()...over...
pool-1-thread-5.temp.show()...over...
pool-1-thread-1.temp.show()...over...
pool-1-thread-3.temp.show()...over...
pool-1-thread-2.temp.show()...over...