暫停執行緒意味著此執行緒還可以恢復執行。
在 Java 多執行緒中,可以使用 suspend() 方法暫停執行緒,使用 resume() 方法恢復執行緒的執行。
suspend() 與 resume() 方法
本節通過一個案例來介紹 suspend() 與 resume() 方法的用法。首先來看一下案例中使用到的 MyThread21 執行緒,程式碼如下所示。
package ch14;
public class MyThread21 extends Thread
{
private long i=0;
public long getI()
{
return i;
}
public void setI(long i)
{
this.i=i;
}
@Override
public void run()
{
while(true)
{
i++;
}
}
}
MyThread21 執行緒中有一個成員 i,其中 setI() 方法和 getI() 方法分別用於設定和獲取 i 的值,run() 方法則是一個從i開始遞增的死迴圈。
下面編寫主執行緒的程式碼,具體如下所示。
package ch14;
public class Test25
{
public static void main(String[] args)
{
try
{
MyThread21 thread=new MyThread21();
thread.start();
Thread.sleep(5000);
//A段
thread.suspend();
System.out.println("A= "+System.currentTimeMillis()+" i= "+thread.getI());
Thread.sleep(5000);
System.out.println("A= "+System.currentTimeMillis()+" i= "+thread.getI());
//B段
thread.resume();
Thread.sleep(5000);
//C段
thread.suspend();
System.out.println("B= "+System.currentTimeMillis()+" i= "+thread.getI());
Thread.sleep(5000);
System.out.println("B= "+System.currentTimeMillis()+" i= "+thread.getI());
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
最終執行結果如下所示。
A= 1540978346179 i= 2680986095
A= 1540978351179 i= 2680986095
B= 1540978356179 i= 5348657508
B= 1540978361179 i= 5348657508
從輸出結果的時間來看,呼叫 suspend() 方法確實可以暫停執行緒,而在呼叫 resume() 方法後執行緒恢復執行狀態。
獨占問題
在使用 suspend() 方法與 resume() 方法時,如果使用不當極易造成公共的同步物件被獨占,從而使得其他執行緒無法存取公共同步物件。
例 2
下面通過一個案例來演示這種情況。如下所示是案例中使用的公共物件的程式碼。
package ch14;
public class SynchronizedObject1
{
synchronized public void printString()
{
System.out.println("begin");
if (Thread.currentThread().getName().equals("a"))
{
System.out.println("a執行緒永遠 suspend了!");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
SynchronizedObject1 類在 printString() 方法開始時輸出“begin”,在該方法結束時輸出“end”,方法體中的if判斷如果當前執行緒的名稱是 a 則呼叫 suspend() 方法暫停執行緒。
接下來編寫主執行緒程式碼,在主執行緒中建立一個 SynchronizedObject1 類物件並線上程中呼叫 printString() 方法,具體程式碼如下所示。
package ch14;
public class Test26
{
public static void main(String[] args)
{
try
{
final SynchronizedObject1 object=new SynchronizedObject1();
Thread thread1=new Thread()
{
@Override
public void run()
{
object.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2=new Thread()
{
@Override
public void run()
{
System.out.println("thread2啟動了,但進入不了printString()方法!所以只會列印1個begin!");
System.out.println("因為printString()方法被a執行緒鎖定並且永遠暫停了!");
object.printString();
}
};
thread2.start();
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
上述程式碼比較簡單這裡就不再解釋,執行後的結果如下所示。從中可以看到由於執行緒被永久暫停,所以只會輸出一個 begin。
begin
a執行緒永遠 suspend了!
thread2啟動了,但進入不了printString()方法!所以只會列印1個begin!
因為printString()方法被a執行緒鎖定並且永遠暫停了!
例 3
還有另外一種獨占鎖的情況也要格外注意,稍有不慎就會掉進“坑”裡。建立測試用的 MyThread22 執行緒,具體程式碼如下:
package ch14;
public class MyThread22 extends Thread
{
private long i=0;
@Override
public void run()
{
while (true)
{
i++;
}
}
}
再來看主執行緒的程式碼,如下所示。
package ch14;
public class Test27
{
public static void main(String[] args)
{
try
{
MyThread22 thread=new MyThread22();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("main end!");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
程式執行後將看到預料不到的結果,如下所示。
main end!
如果將 MyThread22 執行緒類的程式碼更改如下:
package ch14;
public class MyThread22 extends Thread
{
private long i=0;
@Override
public void run()
{
while(true)
{
i++;
System.out.println(i);
}
}
}
再次執行程式,控制台將不列印 main end,執行結果如下所示。
......
130862
130863
130864
130865
130866
130867
出現這種情況的原因是,當程式執行到 println() 方法內部停止時,同步鎖未被釋放。這導致當前 PrintStream 物件的 println() 方法一直呈“暫停”狀態,並且“鎖未釋放”,而 main() 方法中的程式碼“System.out. println(Mmain end!'1);”遲遲不能執行列印。
提示:雖然 suspend() 方法是過期作廢的方法,但還是有必要研究它過期作廢的原因,這是很有意義的。
不同步問題
在使用 suspend() 方法與 resume() 方法時也容易出現因為執行緒的暫停而導致資料不同步的情況。
例 4
下面通過一個案例來演示這種情況。如下所示是案例中使用的公共物件的程式碼。
package ch14;
public class MyObject
{
private String username="1";
private String password="11";
public void setValue(String u,String p)
{
this.username=u;
if(Thread.currentThread().getName().equals("a"))
{
System.out.println("停止a執行緒!");
Thread.currentThread().suspend();
}
this.password=p;
}
public void printUsernamePassword()
{
System.out.println(username+" "+password);
}
}
如上述程式碼所示,MyObject 類的 setValue() 方法會線上程名稱是 a 時執行停止執行緒操作。 如下所示是主執行緒程式碼。
package ch14;
public class Test28
{
public static void main(String[] args) throws InterruptedException
{
final MyObject myobject=new MyObject();
Thread thread1=new Thread()
{
public void run()
{
myobject.setValue("a","aa");
};
};
thread1.setName("a");
thread1.start();
Thread.sleep(500);
Thread thread2=new Thread()
{
public void run()
{
myobject.printUsernamePassword();
};
};
thread2.start();
}
}
程式執行結果如下所示。
停止a執行緒!
a 11
從程式執行的結果可以看到,出現了值不同步的情況,所以在程式中使用 suspend() 方法要格外注意。