徹底明白Java的多執行緒-執行緒間的通訊(2)(轉)

ba發表於2007-08-15
徹底明白Java的多執行緒-執行緒間的通訊(2)(轉)[@more@]2.4 sleep()和yield()的區別
1) sleep()使當前執行緒進入停滯狀態,所以執行sleep()的執行緒在指定的時間內肯定不會執行;yield()只是使當前執行緒重新回到可執行狀態,所以執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行。
2) sleep()可使優先順序低的執行緒得到執行的機會,當然也可以讓同優先順序和高優先順序的執行緒有執行的機會;yield()只能使同優先順序的執行緒有執行的機會。
例15:
class TestThreadMethod extends Thread{
public static int shareVar = 0;
public TestThreadMethod(String name){
super(name);
}
public void run(){
for(int i=0; i<4; i++){
System.out.print(Thread.currentThread().getName());
System.out.println(" : " + i);
//Thread.yield(); (1)
/* (2) */
try{
Thread.sleep(3000);
}
catch(InterruptedException e){
System.out.println("Interrupted");
}

}
}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
TestThreadMethod t2 = new TestThreadMethod("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
執行結果為:
t1 : 0
t1 : 1
t2 : 0
t1 : 2
t2 : 1
t1 : 3
t2 : 2
t2 : 3
由結果可見,透過sleep()可使優先順序較低的執行緒有執行的機會。註釋掉程式碼(2),並去掉程式碼(1)的註釋,結果為:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t2 : 0
t2 : 1
t2 : 2
t2 : 3
可見,呼叫yield(),不同優先順序的執行緒永遠不會得到執行機會。
2.5 join()
使呼叫join()的執行緒執行完畢後才能執行其它執行緒,在一定意義上,它可以實現同步的功能。
例16:
class TestThreadMethod extends Thread{
public static int shareVar = 0;
public TestThreadMethod(String name){
super(name);
}
public void run(){
for(int i=0; i<4; i++){
System.out.println(" " + i);
try{
Thread.sleep(3000);
}
catch(InterruptedException e){
System.out.println("Interrupted");
}
}
}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
t1.start();
try{
t1.join();
}
catch(InterruptedException e){}
t1.start();
}
}
執行結果為:
0
1
2
3
0
1
2
3


3. class Object下常用的執行緒函式
wait()、notify()和notifyAll()這三個函式由java.lang.Object類提供,用於協調多個執行緒對共享資料的存取。
3.1 wait()、notify()和notifyAll()
1) wait()函式有兩種形式:第一種形式接受一個毫秒值,用於在指定時間長度內暫停執行緒,使執行緒進入停滯狀態。第二種形式為不帶引數,代表waite()在notify()或notifyAll()之前會持續停滯。
2) 當對一個物件執行notify()時,會從執行緒等待池中移走該任意一個執行緒,並把它放到鎖標誌等待池中;當對一個物件執行notifyAll()時,會從執行緒等待池中移走所有該物件的所有執行緒,並把它們放到鎖標誌等待池中。
3) 當呼叫wait()後,執行緒會釋放掉它所佔有的“鎖標誌”,從而使執行緒所在物件中的其它synchronized資料可被別的執行緒使用。
例17:
下面,我們將對例11中的例子進行修改
class TestThreadMethod extends Thread{
public static int shareVar = 0;
public TestThreadMethod(String name){
super(name);
}
public synchronized void run(){
if(shareVar==0){
for(int i=0; i<10; i++){
shareVar++;
if(shareVar==5){
try{
this.wait(); //(4)
}
catch(InterruptedException e){}
}
}
}
if(shareVar!=0){
System.out.print(Thread.currentThread().getName());
System.out.println(" shareVar = " + shareVar);
this.notify(); //(5)
}
}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
TestThreadMethod t2 = new TestThreadMethod("t2");
t1.start(); //(1)
//t1.start(); (2)
t2.start(); //(3)
}
}
執行結果為:
t2 shareVar = 5
因為t1和t2是兩個不同物件,所以執行緒t2呼叫程式碼(5)不能喚起執行緒t1。如果去掉程式碼(2)的註釋,並註釋掉程式碼(3),結果為:
t1 shareVar = 5
t1 shareVar = 10
這是因為,當程式碼(1)的執行緒執行到程式碼(4)時,它進入停滯狀態,並釋放物件的鎖狀態。接著,程式碼(2)的執行緒執行run(),由於此時 shareVar值為5,所以執行列印語句並呼叫程式碼(5)使程式碼(1)的執行緒進入可執行狀態,然後程式碼(2)的執行緒結束。當程式碼(1)的執行緒重新執行後,它接著執行for()迴圈一直到shareVar=10,然後列印shareVar。
3.2 wait()、notify()和synchronized
waite()和notify()因為會對物件的“鎖標誌”進行操作,所以它們必須在synchronized函式或synchronized  block中進行呼叫。如果在non-synchronized函式或non-synchronized block中進行呼叫,雖然能編譯透過,但在執行時會發生IllegalMonitorStateException的異常。
例18:
class TestThreadMethod extends Thread{
public int shareVar = 0;
public TestThreadMethod(String name){
super(name);
new Notifier(this);
}
public synchronized void run(){
if(shareVar==0){
for(int i=0; i<5; i++){
shareVar++;
System.out.println("i = " + shareVar);
try{
System.out.println("wait......");
this.wait();
}
catch(InterruptedException e){}
}
}
}
}
class Notifier extends Thread{
private TestThreadMethod ttm;
Notifier(TestThreadMethod t){
ttm = t;
start();
}
public void run(){
while(true){
try{
sleep(2000);
}
catch(InterruptedException e){}
/*1 要同步的不是當前物件的做法 */
synchronized(ttm){
System.out.println("notify......");
ttm.notify();
}
}
}
}
public class TestThread{
public static void main(String[] args){
TestThreadMethod t1 = new TestThreadMethod("t1");
t1.start();
}
}
執行結果為:
i = 1
wait......
notify......
i = 2
wait......
notify......
i = 3
wait......
notify......
i = 4
wait......
notify......
i = 5
wait......
notify......
4. wait()、notify()、notifyAll()和suspend()、resume()、sleep()的討論
4.1 這兩組函式的區別
1) wait()使當前執行緒進入停滯狀態時,還會釋放當前執行緒所佔有的“鎖標誌”,從而使執行緒物件中的synchronized資源可被物件中別的執行緒使用;而suspend()和sleep()使當前執行緒進入停滯狀態時不會釋放當前執行緒所佔有的“鎖標誌”。
2) 前一組函式必須在synchronized函式或synchronized block中呼叫,否則在執行時會產生錯誤;而後一組函式可以non-synchronized函式和synchronized block中呼叫。
4.2 這兩組函式的取捨
Java2已不建議使用後一組函式。因為在呼叫wait()時不會釋放當前執行緒所取得的“鎖標誌”,這樣很容易造成“死鎖”。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-958351/,如需轉載,請註明出處,否則將追究法律責任。

相關文章