37

毎分実行されるバックグラウンドジョブ(EJB 3.1)を作成したいと思います。このために、次の注釈を使用します。

@Schedule(minute = "*/1", hour = "*")

これは正常に機能しています。

ただし、作業に1分以上かかる場合があります。この場合、タイマーはまだ起動されているため、スレッドの問題が発生します。

現在の実行が完了していない場合にスケジューラを終了することは、どういうわけか可能ですか?

4

4 に答える 4

69

同時にアクティブになる可能性があるタイマーが 1 つだけの場合は、いくつかの解決策があります。

まず第一に、@Timerはおそらく に存在するはず@Singletonです。シングルトン メソッドでは、既定で書き込みロックが設定されているため、アクティビティが残っているときにタイマー メソッドを呼び出そうとすると、コンテナーは自動的にロックアウトされます。

基本的には以下で十分です。

@Singleton
public class TimerBean {

    @Schedule(second= "*/5", minute = "*", hour = "*", persistent = false)
    public void atSchedule() throws InterruptedException {

        System.out.println("Called");
        Thread.sleep(10000);
    }
}

atScheduleはデフォルトで書き込みロックされており、コンテナーによって開始された呼び出しを含め、アクティブなスレッドは 1 つだけです。

ただし、ロックアウトされると、コンテナはタイマーを再試行する可能性があるため、これを防ぐには、代わりに読み取りロックを使用し、2 番目の Bean に委譲します (EJB 3.1 では読み取りロックを書き込みロック)。

タイマー Bean:

@Singleton
public class TimerBean {

    @EJB
    private WorkerBean workerBean;

    @Lock(READ)
    @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
    public void atSchedule() {

        try {
            workerBean.doTimerWork();
        } catch (Exception e) {
            System.out.println("Timer still busy");
        }
    }

}

ワーカー Bean:

@Singleton
public class WorkerBean {

    @AccessTimeout(0)
    public void doTimerWork() throws InterruptedException {
        System.out.println("Timer work started");
        Thread.sleep(12000);
        System.out.println("Timer work done");
    }
}

これはまだログにノイズの多い例外を出力する可能性が高いため、より詳細ですが、より静かな解決策は、明示的なブール値を使用することです。

タイマー Bean:

@Singleton
public class TimerBean {

    @EJB
    private WorkerBean workerBean;

    @Lock(READ)
    @Schedule(second = "*/5", minute = "*", hour = "*", persistent = false)
    public void atSchedule() {
        workerBean.doTimerWork();
    }

}

ワーカー Bean:

@Singleton
public class WorkerBean {

    private AtomicBoolean busy = new AtomicBoolean(false);

    @Lock(READ)
    public void doTimerWork() throws InterruptedException {

        if (!busy.compareAndSet(false, true)) {
            return;
        }

        try {
            System.out.println("Timer work started");
            Thread.sleep(12000);
            System.out.println("Timer work done");
        } finally {
            busy.set(false);
        }
    }

}

他にもいくつかのバリエーションが考えられます。たとえば、ビジー チェックをインターセプターに委譲したり、ブール値のみを含むシングルトンをタイマー Bean に挿入して、そのブール値をチェックしたりできます。

于 2013-01-19T12:44:50.737 に答える
7

私は同じ問題に遭遇しましたが、少し違った方法で解決しました。

@Singleton
public class DoStuffTask {

    @Resource
    private TimerService timerSvc;

    @Timeout
    public void doStuff(Timer t) {
        try {
            doActualStuff(t);
        } catch (Exception e) {
            LOG.warn("Error running task", e);
        }
        scheduleStuff();
    }

    private void doActualStuff(Timer t) {

        LOG.info("Doing Stuff " + t.getInfo());
    }

    @PostConstruct
    public void initialise() {
        scheduleStuff();
    }

    private void scheduleStuff() {
        timerSvc.createSingleActionTimer(1000l, new TimerConfig());
    }

    public void stop() {
        for(Timer timer : timerSvc.getTimers()) {
            timer.cancel();
        }
    }

}

これは、将来 (この場合は 1 秒) に実行するタスクを設定することによって機能します。タスクの最後に、タスクを再度スケジュールします。

編集:「もの」を別のメソッドにリファクタリングするように更新して、タイマーの再スケジュールが常に発生するように例外を保護できるようにしました

于 2013-12-06T15:26:35.437 に答える
6

Java EE 7 以降、「EE 対応」のManagedScheduledExecutorService、つまり WildFlyを使用できるようになりました。

たとえば、@Singleton @Startup @LocalBeanで構成されたデフォルトの「managed-scheduled-executor-service」を注入しますstandalone.xml

@Resource
private ManagedScheduledExecutorService scheduledExecutorService;

いくつかのタスクを実行するようにスケジュールします。つまり、固定遅延@PostConstructで毎秒実行します。

scheduledExecutorService.scheduleWithFixedDelay(this::someMethod, 1, 1, TimeUnit.SECONDS);

scheduleWithFixedDelay :

指定された初期遅延の後に最初に有効になり、その後、ある実行の終了と次の開始の間の指定された遅延で有効になる定期的なアクションを作成して実行します。[...]

ie でスケジューラをシャットダウンしないでください@PreDestroy:

Managed Scheduled Executor Service インスタンスはアプリケーション サーバーによって管理されるため、Java EE アプリケーションはライフサイクル関連のメソッドを呼び出すことが禁止されています。

于 2016-10-07T10:09:45.080 に答える
1

私も同様の問題を抱えていました。30 分ごとに実行されるはずのジョブがあり、ジョブが完了するまでに 30 分以上かかる場合がありました。この場合、ジョブの別のインスタンスが開始され、前のインスタンスがまだ終了していませんでした。ジョブが実行を開始するたびにtrueに設定し、終了するたびにfalseに戻す静的ブール変数を持つことで解決しました。静的変数であるため、すべてのインスタンスは常に同じコピーを参照します。静的変数を設定および設定解除するときに、ブロックを同期することもできます。class myjob{ private static boolean isRunning=false;

public executeJob(){
if (isRunning)
    return;
isRunning=true;
//execute job
isRunning=false;
  }

}
于 2015-03-12T17:00:36.403 に答える