2016-11-13 158 views
0

基本上我想在調用方法之後暫停我的線程,然後繼續執行另一個方法。我無法循環,我的方法只能運行一次。 這個想法背後,是用在遊戲中,方法將顯示消息,並且每當用戶按下一個鍵時,就會顯示下一條消息。我不能僅僅通過一個列表,因爲遊戲需要用戶輸入。我看着Thread.pause()Thread.resume()但他們不會工作,並已棄用。 我當前的代碼(這是不工作):在調用方法後暫停線程

private Thread thread; 
private Thread managerThread; 
private final Object lock = new Object(); 

private boolean shouldThreadRun = true; 
private boolean storyRunning = true; 

public Storyline() { 
    setUpThread(); 
} 

private void setUpThread() { 
    managerThread = new Thread(() -> { 
     while(storyRunning) { 
      synchronized (lock) { 
       if(!shouldThreadRun) { 
        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
       System.out.println("Looping"); 
      } 
     } 
    }); 
    thread = new Thread(() -> { 
     synchronized (lock) { 
      pauseThread(); 
      System.out.print("A"); 
      pauseThread(); 
      System.out.print("B"); 
     } 
    }); 
    managerThread.start(); 
    thread.start(); 
} 

public void pauseThread() { 
    shouldThreadRun = false; 
} 
public void resumeThread() { 
    shouldThreadRun = true; 
} 
+0

你不會在我看到的任何地方調用'resumeThread()'。另外,如果要由不同的線程修改和讀取'shouldThreadRun',則應聲明'volatile'。 –

+0

https://docs.oracle.com/javase/tutorial/essential/concurrency/join.html –

+0

因爲我正在測試以查看'B'是否打印過,因爲它不應該打印。但它確實被打印 –

回答

0

看看我的編輯,看看它是否是任何類似於你想要的目的。我正在使用掃描儀來模擬用戶輸入,只需按下鍵盤上的Enter鍵即可進行嘗試。順便說一句,我希望這只是一個練習,而不是現實生活中的情況。您應該儘量避免在實際產品中出現這種低水平的多線程管理,除非確實需要,在這種情況下,您仍然應該使用適當的數據結構。在一個真正的應用程序中,按鈕將被鏈接到回調函數,並且只要按下按鈕,您就會設置一些onClick()方法來執行您需要的代碼。

對於所關注的併發,我強烈建議你看看這些教程:Oracle-Concurrency

PS:請注意,我完全忽略中斷,這是一個不好的做法,這些異常應處理的權利方式:我只是試圖通過保持代碼儘可能簡單來達到預期的結果。另外,就像其他人指出的那樣,您應該通過調用循環內的等待來處理虛假喚醒。

private Thread thread; 
private Thread managerThread; 
private final Object lock = new Object(); 

Scanner in; 

public Storyline() { 
    setUpThread(); 
} 

private void setUpThread() { 
    managerThread = new Thread(() -> { 
     while(true) { 
      in = new Scanner(System.in); 
      in.nextLine(); 
      resumeThread(); 
     } 
    }); 
    thread = new Thread(() -> { 
     synchronized (lock) { 
      while(true){ 
       System.out.print("A"); 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) {} 
       System.out.print("B"); 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) {} 
      } 
     } 
    }); 
    managerThread.start(); 
    thread.start(); 
} 


public void resumeThread() { 
    synchronized(lock){ 
     lock.notify(); 
    } 
} 
+0

我不知道誰低估了這個,它做到了這一招,謝謝! –

+0

我很高興這很有幫助 – c0rtexx

0

的Object.wait,as described in the documentation的第一條規則是,它必須一個循環依賴於它是等待的基礎條件來調用。

所以,你的等待需要看起來像這樣:

synchronized (lock) { 
    while (!shouldThreadRun) { 
     lock.wait(); 
    } 
} 

中斷是不是由事故發生。如果另一個線程明確要求它停止正在執行的操作並完全退出,則線程只會被中斷。

因此,如果你得到一箇中斷,正確的做法是而不是忽略它並打印堆棧跟蹤。你需要乾淨地退出。

做到這一點,最簡單的方法是簡單地附上整個while循環在一個try/catch:

try { 
    while (storyRunning) { 
     synchronized (lock) { 
      while (!shouldThreadRun) { 
       lock.wait(); 
      } 
      System.out.println("Looping"); 
     } 
    } 
} catch (InterruptedException e) { 
    System.out.println("Exiting, because someone asked me to stop."); 
    e.printStackTrace(); 
} 

這樣,你的while循環將自動中斷時退出。

最後,Object.wait是無用的,除非另一個線程在等待線程同步的同一個對象上調用Object.notify或Object.notifyAll。等待方法將(可能)永遠不會返回,除非對象得到通知:

public void pauseThread() { 
    synchronized (lock) { 
     shouldThreadRun = false; 
     // Tell waiting thread that shouldThreadRun may have changed. 
     lock.notify(); 
    } 
} 

public void resumeThread() { 
    synchronized (lock) { 
     shouldThreadRun = true; 
     // Tell waiting thread that shouldThreadRun may have changed. 
     lock.notify(); 
    } 
} 

請注意,同步是在方法內。如果您始終保持線程同步於lock,那麼管理器線程永遠不會有機會運行,因爲它試圖獲取同一對象上的同步鎖。(然而,相反的是不正確的;所述管理器線程可以保持上lock所有的時間同步,因爲等待()方法將暫時釋放同步鎖,從而允許另一個線程進行。)

如果所有訪問shouldThreadRun的代碼位於同步塊內部,因爲同步已確保多線程一致性,所以您不需要(也不應該)使shouldThreadRun易變。