2012-06-14 23 views
2

我想創建一個定期運行某個(可運行)的類,但可以在需要時喚醒它。如果我可以封裝整個事情,我想公開以下方法:創建一個在定時器上運行的java線程,但可以在任何時候喚醒

public class SomeService implements Runnable { 


    public run() { 
    // the code to run at every interval 
    } 

    public static void start() { } 
    public static void wakeup() { } 
    public static void shutdown() { } 

} 

不知何故,我已經得到了這麼多。但我不確定這是否是正確的方法。

public class SomeService implements Runnable { 

    private static SomeService service; 
    private static Thread thread; 
    static { 
    start(); 
    } 

    private boolean running = true; 

    private SomeService() { 
    } 

    public void run() { 
    while (running) { 
     try { 
     // do what needs to be done 
     // perhaps peeking at a blocking queue 
     // or checking for records in a database 
     // trying to be independent of the communication 
     System.out.println("what needs to be done"); 
     // wait for 15 seconds or until notify 
     synchronized (thread) { 
      try { 
      thread.wait(15000); 
      } catch (InterruptedException e) { 
      System.out.println("interrupted"); 
      } 
     } 
     } catch (Exception e) { 
     e.printStackTrace(); 
     } 
    } 
    } 

    private static void start() { 
    System.out.println("start"); 
    service = new SomeService(); 
    thread = new Thread(service); 
    thread.setDaemon(true); 
    thread.start(); 
    } 

    public static void wakeup() { 
    synchronized (thread) { 
     thread.notify(); 
    } 
    } 

    public static void shutdown() { 
    synchronized (thread) { 
     service.running = false; 
     thread.interrupt(); 
     try { 
     thread.join(); 
     } catch (InterruptedException e) { 
     e.printStackTrace(); 
     } 
    } 
    System.out.println("shutdown"); 
    } 

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

    SomeService.wakeup(); 
    System.in.read(); 
    SomeService.wakeup(); 
    System.in.read(); 
    SomeService.shutdown(); 

    } 

} 

我擔心變量應該被聲明爲volatile。並且擔心thread.isInterrupted()需要檢查「需要完成的部分」。這看起來是不是正確的方法?我應該把它翻譯成執行者嗎?我該如何強制執行預定的執行者?

編輯

與執行試驗之後,似乎這種做法似乎是合理的。你怎麼看?

public class SomeExecutorService implements Runnable { 

    private static final SomeExecutorService runner 
    = new SomeExecutorService(); 

    private static final ScheduledExecutorService executor 
    = Executors.newSingleThreadScheduledExecutor(); 

    // properties 

    ScheduledFuture<?> scheduled = null; 

    // constructors 

    private SomeExecutorService() { 
    } 

    // methods 

    public void schedule(int seconds) { 
    scheduled = executor.schedule(runner, seconds, TimeUnit.SECONDS); 
    } 

    public void force() { 
    if (scheduled.cancel(false)) { 
     schedule(0); 
    } 
    } 

    public void run() { 
    try { 
     _logger.trace("doing what is needed"); 
    } catch (Exception e) { 
     _logger.error("unexpected exception", e); 
    } finally { 
     schedule(DELAY_SECONDS); 
    } 
    } 

    // static methods 

    public static void initialize() { 
    runner.schedule(0); 
    } 

    public static void wakeup() { 
    runner.force(); 
    } 

    public static void destroy() { 
    executor.shutdownNow(); 
    } 

} 
+0

如我所知,製作所有靜態方法是一個不好的做法。你爲什麼不只保留'main()''static'並且創建一個實例'service = new SomeService();' – alaster

+0

@alaster我在想這會封裝這個問題,因爲我不需要保留跟蹤創建的「服務」。通過靜態聲明,我可以使用'SomeService.wakeup()'對其進行演示,並使用'SomeService.shutdown()'完成它。由於我只能有一個跑步......這似乎是一個好方法。 – rmarimon

回答

4

對於初學者 - 你可能不想自己實現Runnable;你應該接受一個Runnable。如果您希望將您的類傳遞給其他人執行,則只應實現Runnable。

爲什麼不只是包裝一個ScheduledExecutorService?這是一個快速(非常差,但應該是功能)的實現。

public class PokeableService { 

    private ScheduledExecutorService service = Executors.newScheduledThreadPool(1); 
    private final Runnable codeToRun; 

    public PokeableService (Runnable toRun, long delay, long interval, TimeUnit units) { 
    codeToRun = toRun; 
    service.scheduleAtFixedRate(toRun, delay, interval, units); 
    } 

    public void poke() { 
    service.execute(codeToRun); 
    } 
} 
+0

+1正是我要建議的。 ;) –

+0

poke()也應該移除計劃的任務,否則計劃的任務將在比規定的時間間隔更短的時間內執行戳穿任務。 –

+0

捅戳是否以固定的速度取消預定?執行和計劃可以同時運行嗎? – rmarimon

1

變量不需要是易失性的,因爲它們是在同步塊中讀取和修改的。

您應該爲鎖定使用不同的對象,然後是線程,因爲Thread類是自己的同步。

我建議使用單線程的ScheduledExecutorService並刪除睡眠。然後,如果您想在當前睡眠期間運行任務,則可以再次將其提交給執行程序以進行一次運行。只需使用ScheduledExecutorService擴展的ExecutorService中的execute或submit方法即可。

關於isInterrupted的檢查,如果do工作部分需要花費很多時間,可以在中間取消,並且不調用阻塞並會以任何方式拋出中斷異常的方法,您應該這樣做。

0

使用等待/通知應該是更有效的方法。我也同意這樣一個建議,即使用'volatile'並不是必要的,並且在另一個對象上同步會避免衝突。

其他一些建議:

  • 別處啓動線程,從靜態塊開始是不是好的做法
  • 把執行邏輯中的「執行()」方法或類似希望

此代碼實現上述建議。還要注意,只有一個線程執行SomeService執行邏輯,並且它將在上次完成時間後發生INTERVAL毫秒。在手動觸發wakeUp()調用後,您不應得到重複執行。

public class SomeService implements Runnable { 

    private static final INTERVAL = 15 * 1000; 
    private Object svcSynchronizer = new Object(); 
    private boolean running = true; 

    private SomeService() { 
    } 

    public void run() { 
    while (running) { 
     try { 
     // do what needs to be done 
     // perhaps peeking at a blocking queue 
     // or checking for records in a database 
     // trying to be independent of the communication 
     System.out.println("what needs to be done"); 

     // wait for 15 seconds or until notify 
     try { 
      svcSynchronizer.wait(INTERVAL); 
     } catch (InterruptedException e) { 
      // ignore interruptions 
     } 

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


    public void wakeUp() { 
    svcSynchronizer.notifyAll(); 
    } 

    public void shutdown() { 
    running = false; 
    svcSynchronizer.notifyAll(); 
    } 

} 
+0

你將如何處理整個事件的關閉? – rmarimon

+0

添加了關閉操作的示例,該操作會導致從.wait()調用中退出,並且while(running)循環將退出。 –