2017-05-29 14 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());を呼び出すことができます、またはあなたはあなたのコード内の好きな場所他。

には実行するスレッドが1つしかないため、一度に起動するのは1回だけです(doWhatever())。そして、あなたのボタンプレスハンドラは、新しいオブジェクトをキューに置くだけで何もしないので、待つ必要はありません。

+1

ありがとう!それは正しい方向に私を得た。 'ScheduledThreadPoolExecutor'を使用するタイマーの代わりに' scheduleWithFixedDelay'で作業メソッドを呼び出すと、私の要求によく合います。 – Roland

3

私は定期的にコードを実行するスレッドを持っている、E。 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(); 
} 

通知はすぐにスレッドを呼び起こしますそれは仕事です。それで、作業方法はスレッドしか実行していないので、​​である必要はありません。

注:としては、特定のOSスレッドの実装で発生する可能性がスプリアスウェイクアップに苦しむ可能性があり、このようなwait()を使用して、@shinobiによって指摘しました。

最後に、Executor implement Runnable as opposed to extending Threadを作成することをお勧めします。

+0

美しく最小限でシンプルですが、偽の起床から保護されていません(https://stackoverflow.com/questions/13148001/spurious-wakeups-wait-and-notifyall)。 – shinobi

+0

良い点。余計な走りが問題であるかどうかは、@ shinobiに依存していると思います。 – Gray

0

共有サーバスレッド(タスクを実行1)とクライアントスレッド(即時実行をトリガするために必要なもの)との間にセマフォ:

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()(定期的な実行の10Sまたはタイミングアウト(即時実行をトリガー)のいずれかとしてセマフォから許可証を取得すると、タスクを実行します離れて、エンドツースタート。)実行は10秒離れているstart-to-start最後の実行の開始時間を追跡するだけでなく、やや複雑なロジックを取りますが、基本的な考え方は変わりません。

実行中に即時実行のためにトリガされる可能性がある場合、タスクの複数のバックツーバック実行を避けるために、毎回許可をなくす必要があります。

関連する問題