2013-12-11 110 views
0

我正在實現以下Java接口以允許線程暫停和恢復。我有一個使用wait()/ notifyAll()的工作版本,但我想知道是否有更簡單的方法來執行它(例如,在java.util.concurrent中使用一些漂亮的小部件)?避免在實用程序中等待/通知掛起/恢復線程

public interface Suspender { 

    /** 
    * Go into pause mode: any threads which subsequently call maybePause() 
    * will block. Calling pause() if already in pause mode has no effect. 
    */ 
    void pause(); 

    /** 
    * Leave pause mode: any threads which call maybePause() will not block, 
    * and any currently paused threads will be unblocked. Calling resume() 
    * if not in pause mode has no effect. 
    */ 
    void resume(); 

    /** 
    * If the Suspender is in pause mode, block, and only resume execution 
    * when the Suspender is resumed. Otherwise, do nothing. 
    */ 
    void maybePause(); 

} 

回答

0

是的,但通過體系結構。

class Suspender { 

    protected static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()); 
    protected final Runnable task; 
    protected final long period; 
    protected final TimeUnit unit; 
    private Future t; 

    public Suspender(final Runnable task, final long initialDelay, final long period, final TimeUnit unit) { 
    this.task = task; 
    this.period = period; 
    this.unit = unit; 
    t = executor.scheduleAtFixedRate(task, initialDelay, period, unit); 
    } 

    public boolean pause() { 
    if (t == null) { 
     return false; 
    } 
    if (t.cancel(true)) { 
     t = null; 
     return true; 
    } 
    return false; 
    } 

    public boolean resume() { 
    if (t == null) { 
     t = executor.scheduleAtFixedRate(task, 0, period, unit); 
     return true; 
    } 
    return false; 
    } 
} 

所以它所做的就是基於您的暫停/恢復調用基本上安排和取消可運行。

很明顯,不是暫停一個線程中任務,這是一件好事,因爲如果你可以在執行期間暫停一個線程,它可能會導致各種問題。這從未發佈的鎖開始,但也包括半開網絡連接,半寫文件等。

因此,只要您有一個具有單個任務的線程,請不要暫停它。只有當你有一個執行重複任務的線程(因此是ScheduledExecutorService)時,你可以跳過該任務的進一步調用。然後,您的線程可以通過在暫停/取消合理可能的代碼狀態期間查詢Thread.interrupted()標誌,在內部決定是否暫停。

+0

總是值得關注一個問題的不同方法,但Suspender接口對我的用例非常適用。我更感興趣的是如何實現該接口而不訴諸低級別的併發原語。 –

0

這個實現可以容易地建立在持有1個許可證的java.uitl.concurrent.Semaphore之上。

  • pause()需要許可證再次(阻塞,直到它得到它)
  • resume()釋放許可證。
  • maybePause()取得許可證並阻止,直到它得到它。然後再次釋放它。

要處理同一個Suspender上的多個暫停請求,可以使用另一個信號作爲更改暫停狀態的權限,並在暫停和恢復方法中使用該信號。

編輯

好了,一旦我寫作,我意識到它並不像我想象的那麼簡單。不過我還是設法通過寫一個實現(甚至只是一個)信號燈:

@ThreadSafe 
public class SuspenderImpl implements Suspender { 

    @GuardedBy("stateLock") 
    private boolean paused; 
    @GuardedBy("stateLock") 
    private int pausedThreads; 
    private final Semaphore token = new Semaphore(0); 
    private final Object stateLock = new Object(); 

    @Override 
    public void pause() { 
     synchronized (stateLock) { 
      paused = true; 
     } 
    } 

    @Override 
    public void resume() { 
     synchronized (stateLock) { 
      paused = false; 
      token.release(pausedThreads); 
      pausedThreads = 0; 
     } 
    } 

    @Override 
    public void maybePause() { 
     synchronized (stateLock) { 
      if (paused) { 
       pausedThreads++; 
      } else { 
       token.release(); 
      } 
     } 
     try { 
      token.acquire(); 
     } catch (InterruptedException e) { 
      Thread.currentThread().interrupt(); 
     } 
    } 
} 

的基本思想顯著從我原來的不同。在這個實現中,maybePause()方法根據暫停的布爾標誌決定是否立即釋放許可證。如果設置,信號量和立即獲得許可證。如果未設置,暫停線程的數量會增加,信號量的獲取將被阻止。

pause()方法只是設置標誌。 resume()方法將該標誌設置爲false,釋放許可等於暫停的線程數並將其設置爲零。

所有可變狀態都由內部鎖保護。

+0

我認爲這種方法存在一個問題 - 如果以sempahore.tryAcquire()方式實現pause(),那麼它可能是不吉利的,並嘗試將它作爲maybePause()的中間部分,結果最終不會當你應該進入暫停模式時。 –

+0

你是對的,我編輯了我的答案。 – bowmore

+0

謝謝...你有一個實現顯示(2信號版本)如何工作? –