Java併發程式設計之三:執行緒掛起、恢復與終止的正確方法
阿新 • • 發佈:2019-02-04
出處:http://blog.csdn.NET/ns_code/article/details/17095733
掛起和恢復執行緒
Thread 的API中包含兩個被淘汰的方法,它們用於臨時掛起和重啟某個執行緒,這些方法已經被淘汰,因為它們是不安全的,不穩定的。如果在不合適的時候掛起執行緒(比如,鎖定共享資源時),此時便可能會發生死鎖條件——其他執行緒在等待該執行緒釋放鎖,但該執行緒卻被掛起了,便會發生死鎖。另外,在長時間計算期間掛起執行緒也可能導致問題。
下面的程式碼演示了通過休眠來延緩執行,模擬長時間執行的情況,使執行緒更可能在不適當的時候被掛起:
-
publicclass DeprecatedSuspendResume
- //volatile關鍵字,表示該變數可能在被一個執行緒使用的同時,被另一個執行緒修改
- privatevolatileint firstVal;
- privatevolatileint secondVal;
- //判斷二者是否相等
- publicboolean areValuesEqual(){
- return ( firstVal == secondVal);
- }
- publicvoid run() {
- try{
-
firstVal = 0
- secondVal = 0;
- workMethod();
- }catch(InterruptedException x){
- System.out.println("interrupted while in workMethod()");
- }
- }
- privatevoid workMethod() throws InterruptedException {
- int val = 1;
-
while (true){
- stepOne(val);
- stepTwo(val);
- val++;
- Thread.sleep(200); //再次迴圈錢休眠200毫秒
- }
- }
- //賦值後,休眠300毫秒,從而使執行緒有機會在stepOne操作和stepTwo操作之間被掛起
- privatevoid stepOne(int newVal) throws InterruptedException{
- firstVal = newVal;
- Thread.sleep(300); //模擬長時間執行的情況
- }
- privatevoid stepTwo(int newVal){
- secondVal = newVal;
- }
- publicstaticvoid main(String[] args){
- DeprecatedSuspendResume dsr = new DeprecatedSuspendResume();
- Thread t = new Thread(dsr);
- t.start();
- //休眠1秒,讓其他執行緒有機會獲得執行
- try {
- Thread.sleep(1000);}
- catch(InterruptedException x){}
- for (int i = 0; i < 10; i++){
- //掛起執行緒
- t.suspend();
- System.out.println("dsr.areValuesEqual()=" + dsr.areValuesEqual());
- //恢復執行緒
- t.resume();
- try{
- //執行緒隨機休眠0~2秒
- Thread.sleep((long)(Math.random()*2000.0));
- }catch(InterruptedException x){
- //略
- }
- }
- System.exit(0); //中斷應用程式
- }
- }
從areValuesEqual()返回的值有時為true,有時為false。以上程式碼中,在設定firstVal之後,但在設定secondVal之前,掛起新執行緒會產生麻煩,此時輸出的結果會為false(情況1),這段時間不適宜掛起執行緒,但因為執行緒不能控制何時呼叫它的suspend方法,所以這種情況是不可避免的。
當然,即使執行緒不被掛起(註釋掉掛起和恢復執行緒的兩行程式碼),如果在main執行緒中執行asr.areValuesEqual()進行比較時,恰逢stepOne操作執行完,而stepTwo操作還沒執行,那麼得到的結果同樣可能是false(情況2)。
下面我們給出不用上述兩個方法來實現執行緒掛起和恢復的策略——設定標誌位。通過該方法實現執行緒的掛起和恢復有一個很好的地方,就是可以線上程的指定位置實現執行緒的掛起和恢復,而不用擔心其不確定性。
對於上述程式碼的改進程式碼如下:
- publicclass AlternateSuspendResume extends Object implements Runnable {
- privatevolatileint firstVal;
- privatevolatileint secondVal;
- //增加標誌位,用來實現執行緒的掛起和恢復
- privatevolatileboolean suspended;
- publicboolean areValuesEqual() {
- return ( firstVal == secondVal );
- }
- publicvoid run() {
- try {
- suspended = false;
- firstVal = 0;
- secondVal = 0;
- workMethod();
- } catch ( InterruptedException x ) {
- System.out.println("interrupted while in workMethod()");
- }
- }
- privatevoid workMethod() throws InterruptedException {
- int val = 1;
- while ( true ) {
- //僅當賢臣掛起時,才執行這行程式碼
- waitWhileSuspended();
- stepOne(val);
- stepTwo(val);
- val++;
- //僅當執行緒掛起時,才執行這行程式碼
- waitWhileSuspended();
- Thread.sleep(200);
- }
- }
- privatevoid stepOne(int newVal)
- throws InterruptedException {
- firstVal = newVal;
- Thread.sleep(300);
- }
- privatevoid stepTwo(int newVal) {
- secondVal = newVal;
- }
- publicvoid suspendRequest() {
- suspended = true;
- }
- publicvoid resumeRequest() {
- suspended = false;
- }
- privatevoid waitWhileSuspended()
- throws InterruptedException {
- //這是一個“繁忙等待”技術的示例。
- //它是非等待條件改變的最佳途徑,因為它會不斷請求處理器週期地執行檢查,
- //更佳的技術是:使用Java的內建“通知-等待”機制
- while ( suspended ) {
- Thread.sleep(200);
- }
- }
- publicstaticvoid main(String[] args) {
- AlternateSuspendResume asr =
- new AlternateSuspendResume();
- Thread t = new Thread(asr);
- t.start();
- //休眠1秒,讓其他執行緒有機會獲得執行
- try { Thread.sleep(1000); }
- catch ( InterruptedException x ) { }
- for ( int i = 0; i < 10; i++ ) {
- asr.suspendRequest();
- //讓執行緒有機會注意到掛起請求
- //注意:這裡休眠時間一定要大於
- //stepOne操作對firstVal賦值後的休眠時間,即300ms,
- //目的是為了防止在執行asr.areValuesEqual()進行比較時,
- //恰逢stepOne操作執行完,而stepTwo操作還沒執行
- try { Thread.sleep(350); }
- catch ( InterruptedException x ) { }
- System.out.println("dsr.areValuesEqual()=" +
- asr.areValuesEqual());
- asr.resumeRequest();
- try {