2017-05-29 56 views
1

我有一個定期執行代碼的線程, G。每10秒鐘一次。我希望可以選擇以自發方式調用相同的代碼,而不必等待10秒鐘。但是自動執行和自發執行的代碼不能同時運行,如果用戶在調用相同方法時按下執行按鈕,它們應該按順序運行。在週期性運行的線程中自動執行代碼

有沒有人知道一個好的模式,甚至可以解決這種需求的類?

首先想到的是使工作方法同步。但是在這種情況下,手動執行(例如按下按鈕)被阻塞,並且必須等待直到線程中的方法結束。有沒有更好的方法沒有阻塞?

實施例:

public class Executor extends Thread { 

    // endless loop, executes work method periodically with pause inbetween 
    @Override 
    public void run() { 

     while(true) { 

      work("automatic"); 

      pause(10000); 

     } 

    } 

    // Working method that's executed periodically or manually 
    private synchronized void work(String text) { 

     System.out.println("Working " + text + " " + System.currentTimeMillis()); 

    } 

    // helper method that pauses the thread 
    private static void pause(long sleepMs) { 
     try { 

      Thread.sleep(sleepMs); 

     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static void main(String[] args) { 

     // start automatic execution 
     Executor executor = new Executor(); 
     executor.start(); 

     // pause a while 
     pause(1000); 

     // manual execution 
     executor.work("manual"); 

    } 

} 

編輯:解我的要求:

public class ScheduledExecutor { 

    public static void main(String[] args) throws InterruptedException { 

     ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1); 
     executor.scheduleWithFixedDelay(new Work("auto"), 0, 10, TimeUnit.SECONDS); 

     Thread.sleep(1000); 

     executor.execute(new Work("manual")); 

    } 

    public static class Work implements Runnable { 

     String text; 

     public Work(String text) { 
      this.text = text; 
     } 

     @Override 
     public void run() { 

      System.out.println("Working " + text + " " + System.currentTimeMillis()); 

     } 
    } 

} 
+0

中斷睡眠? –

+0

如果線程在非休眠模式下被中斷會發生什麼? – Roland

+0

您是否可以更新您的問題以清楚說明您希望如何工作。你說你不希望手動和自動的線程代碼同時運行。然後你說你不想在按下按鈕時手動執行被阻止...... – Allan

回答

3

我會創造一個新的,單獨的線程執行服務:

ExecutorService executorService = Executors.newFixedThreadPool(1); 

然後,我會成立哺養executorService任務每10秒一次的計時器。

new Timer(10000, new ActionListener { 
    public void actionPerformed(ActionEvent evt) { 
     executorService.execute(() -> doWhatever()); 
    } 
}).start(); 

最後,你可以在你按下按鈕的處理程序,或其他任何地方,你想在你的代碼中調用executorService.execute(() -> doWhatever());

只有一次激活doWhatever()會一次運行,因爲executorService只有一個線程可以運行它們。而且,您的按鈕按下處理程序將永遠不必等待,因爲它只是在隊列中放置一個新對象。

+1

謝謝!這讓我走向了正確的方向。而不是使用'ScheduledThreadPoolExecutor'並使用'scheduleWithFixedDelay'調用工作方法的Timer與我的需求更好地匹配。 – Roland

3

我有其週期性地執行代碼的線程,即G。每10秒鐘一次。我希望可以選擇以自發方式調用相同的代碼,而不必等待10秒鐘。

一種簡單的方法在你的代碼來做到這一點並不用Thread.sleep(...)而是做wait(...)暫停。然後,只要你想讓命令喚醒並手動運行,它就會執行一個notify()

所以,你的代碼看起來是這樣的:

while(true) { 
    work("automatic"); 
    synchronized (this) { 
     try { 
      // wait for a bit but allow someone else to awake us to run manually 
      wait(10000); 
     } catch (InterruptedException ie) { 
      // always a good pattern 
      Thread.currentThread().interrupt(); 
      return; 
     } 
    } 
} 

然後,當你想擁有它的手動運行你這樣做:

synchronized (executor) { 
    executor.notify(); 
} 

的通知將立即喚醒線程,以便它可以運行這是任務。那麼工作方法不需要是​​,因爲只有Executor線程正在運行它。

注:正如指出的@shinobi,使用wait()這樣可以從虛假的喚醒,可與某些操作系統線程實現發生受苦。

最後,製作Executor implement Runnable as opposed to extending Thread是一種更好的做法。

+0

精美簡約,但不防範虛假喚醒(https://stackoverflow.com/questions/13148001/spurious-wakeups-wait-and-notifyall)。 – shinobi

+0

好點。猜猜這取決於是否額外運行是一個問題@shinobi。 – Gray

0

共享服務器線程(執行任務的名稱)和客戶端線程(需要觸發立即執行的那些)之間的信號:

Semaphore sem = new Semaphore(0); 

服務器線程需要執行下面的代碼(請注意,這是一個無限循環—你可能會想插件程序終止支票的條件while()):

while(true) { 
     try { 
      sem.tryAcquire(10, TimeUnit.SECONDS); 
     } catch(InterruptedException e) { 
      continue; 
     } 

     runTask(); 
     sem.drainPermits(); 
    } 

然後,爲了觸發立即執行,客戶端線程需要這樣做:

sem.release(); 

因此,服務器線程將盡快爲客戶端線程釋放一個(觸發立即執行)或定時出在Semaphore.tryAcquire()(定期執行的10秒後要麼取得從信號量許可證執行任務從頭開始。)執行間隔10s 開始啓動將採取一些稍微更多的涉及邏輯,以及跟蹤上次執行的開始時間,但基本思路保持不變。

爲了避免多次背靠背執行任務,每次都需要排空許可證,以防在執行過程中立即執行時觸發執行。