Sto usando JSP/Servlet su Apache Tomcat. Devo eseguire un metodo ogni 10 minuti. Come posso raggiungere questo obiettivo?Come eseguire un metodo di lavoro in background a intervalli fissi?
risposta
Leggi su ScheduledExecutorService esso deve essere iniziata da un ServletContextListener
public class MyContext implements ServletContextListener
{
private ScheduledExecutorService sched;
@Override
public void contextInitialized(ServletContextEvent event)
{
sched = Executors.newSingleThreadScheduledExecutor();
sched.scheduleAtFixedRate(new MyTask(), 0, 10, TimeUnit.MINUTES);
}
@Override
public void contextDestroyed(ServletContextEvent event)
{
sched.shutdownNow();
}
}
Inoltre, si potrebbe provare a utilizzare il Java Timer da un ServletContextListener ma non è raccomandato in un container Java EE dal momento che toglie il controllo del Infilare le risorse dal contenitore. (la prima opzione con ScheduledExecutorService è la strada da percorrere).
Timer timer = new Timer("MyTimer");
MyTask t = new MyTask();
//Second Parameter is the specified the Starting Time for your timer in
//MilliSeconds or Date
//Third Parameter is the specified the Period between consecutive
//calling for the method.
timer.schedule(t, 0, 1000*60*10);
E MyTask
che implementa TimerTask
è una classe che implementa l'interfaccia Runnable
quindi bisogna ignorare il metodo run con il tuo codice:
class MyTask extends TimerTask
{
public void run()
{
// your code here
}
}
Come sei su Tomcat, che è solo un barebone servletcontainer, non è possibile utilizzare EJB @Schedule
per questo, che è raccomandato dalla specifica Java EE. La soluzione migliore è quindi il ScheduledExecutorService
dal pacchetto java.util.concurrent
di Java 1.5. È possibile attivare questo con l'aiuto di un ServletContextListener
come segue:
@WebListener
public class BackgroundJobManager implements ServletContextListener {
private ScheduledExecutorService scheduler;
@Override
public void contextInitialized(ServletContextEvent event) {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(new SomeTask(), 0, 10, TimeUnit.MINUTES);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
scheduler.shutdownNow();
}
}
dove lo sguardo SomeTask
classe come questa:
public class SomeTask implements Runnable {
@Override
public void run() {
// Do your job here.
}
}
Se stavi in realtà utilizzando un vero e proprio contenitore Java EE con il supporto EJB e tutti su em (come Glassfish, JBoss AS, TomEE, ecc.), quindi è possibile utilizzare un EJB @Singleton
con un metodo @Schedule
. In questo modo il contenitore si preoccuperà di raggruppare e distruggere i thread. Tutto ciò che serve è allora la seguente EJB:
@Singleton
public class SomeTask {
@Schedule(hour="*", minute="*/10", second="0", persistent=false)
public void run() {
// Do your job here.
}
}
Si noti che in questo modo è possibile continuare a utilizzare in modo trasparente contenitore gestito transazioni solito modo (@PersistenceContext
e così via), che non è possibile con ScheduledExecutorService
— Avresti per ottenere manualmente il gestore di entità e avviare manualmente/commit/terminare la transazione, ma per impostazione predefinita non si dispone già di un'altra opzione su un servlet contenitore barebone come Tomcat.
Si noti che non si dovrebbe mai usare un Timer
in un'applicazione Web Java EE apparentemente "a vita" in esecuzione. Ha i seguenti problemi principali che lo rende inadatto per l'uso in Java EE (citato da Java Concurrency in Practice):
Timer
è sensibile ai cambiamenti del clock di sistema,ScheduledExecutorService
non è.Timer
ha solo un thread di esecuzione, quindi un'attività a esecuzione prolungata può ritardare altre attività.ScheduledExecutorService
può essere configurato con qualsiasi numero di thread.- Qualsiasi eccezione di runtime generata in un
TimerTask
uccide quello thread, rendendo cosìTimer
morto, vale a dire che le attività pianificate non verranno più eseguite (finché non si riavvia il server).ScheduledThreadExecutor
non solo rileva le eccezioni di runtime, ma ti consente di gestirle se lo desideri. L'attività che ha generato un'eccezione verrà annullata, ma altre attività continueranno a essere eseguite.
Ottima risposta, grazie @BalusC. Una domanda però: come modificherei lo scheduler se avessi bisogno di cambiare l'intervallo tra le chiamate? ad es. Ho un po 'di frequenza' che può essere impostata dall'utente in webgui che rappresenta il tempo tra le esecuzioni in secondi. – Gewure
Ok, ho capito. Ma da dove devo chiamare questo metodo. Devo avviarlo non appena la mia applicazione è attiva –
[Non usare mai 'Timer' in Java EE] (http://stackoverflow.com/questions/9173132/stop-scheduled-timer-when-shutdown-tomcat/9186070#9186070). – BalusC
Grazie a @BalusC ho menzionato esplicitamente che nella mia risposta. – user1697575