Come creare un thread in esecuzione tutto il tempo che la mia applicazione è in esecuzione
-
05-07-2019 - |
Domanda
EDIT:
Ora sono sicuro che il problema è legato al
while (true)
contiene tutti gli altri comandi mentre l'ho commentato e l'applicazione viene distribuita senza l'eccezione allegata. Non sono sicuro di quanto sia importante, ma la mia implementazione ServletContextListener
è simile a questa:
la classe pubblica BidPushService implementa ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
//Some init code not relevant, omitted for clarity
BidPushThread t= new BidPushThread();
t.setServletContext(sce.getServletContext());
t.run();
}
Quindi ora il thread viene eseguito quando viene distribuita l'app, ma poiché il ciclo while
è commentato, non ha alcun significato reale.
Devo eseguire un thread in background quando l'applicazione viene caricata e controllare costantemente (senza un timeout) una determinata coda per gli oggetti. Naturalmente, una volta che ci sono oggetti, "si prende cura di loro" e quindi continua a controllare la coda.
Attualmente sto implementando l'interfaccia ServletContextListener
e mi viene chiamato quando l'app viene caricata. In esso, faccio alcune cose di manutenzione e inizio un thread che ho ereditato da java.lang.Thread
.
Ecco dove inizia il mio problema (o almeno così penso). Nel mio metodo run ()
, ho un
while (true) {
//some code which doesn't put the thread to sleep ever
}
Quando provo a distribuire la mia app sul server ricevo un java.util.concurrent.TimeOutException
.
Cosa sto sbagliando?
Non posso avere un thread sempre in esecuzione? Quando l'app viene rimossa, quel thread viene interrotto dall'evento corrispondente nel mio ServletContextListener
.
Ho davvero bisogno di qualcosa che continui a controllare la coda senza indugio.
Grazie mille per qualsiasi aiuto!
Modifica: questa è la traccia dello stack
GlassFish: deploy is failing=
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishDeployedDirectory(SunAppServerBehaviour.java:710)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishModuleForGlassFishV3(SunAppServerBehaviour.java:569)
at com.sun.enterprise.jst.server.sunappsrv.SunAppServerBehaviour.publishModule(SunAppServerBehaviour.java:266)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publishModule(ServerBehaviourDelegate.java:948)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publishModules(ServerBehaviourDelegate.java:1038)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publish(ServerBehaviourDelegate.java:872)
at org.eclipse.wst.server.core.model.ServerBehaviourDelegate.publish(ServerBehaviourDelegate.java:708)
at org.eclipse.wst.server.core.internal.Server.publishImpl(Server.java:2690)
at org.eclipse.wst.server.core.internal.Server$PublishJob.run(Server.java:272)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
Il mio codice:
public class BidPushThread extends Thread {
private ServletContext sc=null;
@Override
public void run() {
if (sc!=null){
final Map<String, List<AsyncContext>> aucWatchers = (Map<String, List<AsyncContext>>) sc.getAttribute("aucWatchers");
BlockingQueue<Bid> aucBids = (BlockingQueue<Bid>) sc.getAttribute("aucBids");
Executor bidExecutor = Executors.newCachedThreadPool();
final Executor watcherExecutor = Executors.newCachedThreadPool();
while(true)
{
try // There are unpublished new bid events.
{
final Bid bid = aucBids.take();
bidExecutor.execute(new Runnable(){
public void run() {
List<AsyncContext> watchers = aucWatchers.get(bid.getAuctionId());
for(final AsyncContext aCtx : watchers)
{
watcherExecutor.execute(new Runnable(){
public void run() {
// publish a new bid event to a watcher
try {
aCtx.getResponse().getWriter().print("A new bid on the item was placed. The current price "+bid.getBid()+" , next bid price is "+(bid.getBid()+1));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
});
}
}
});
} catch(InterruptedException e){}
}
}
}
public void setServletContext(ServletContext sc){
this.sc=sc;
}
}
Ci scusiamo per il disordine di formattazione ma per la durata del mio codice "indent" di 4 spazi " semplicemente non funziona per me Modifica: leggi "BlockingQueue" e implementato, ma sto ancora ottenendo la stessa esatta eccezione e traccia dello stack. ha modificato il codice sopra riportato per riflettere l'uso di 'BlockingQueue'
Soluzione
Il tuo codice non avvia un nuovo thread, esegue il loop nello stesso thread ed è per questo che stai ricevendo un errore di timeout quando distribuito.
Per avviare un thread devi chiamare il metodo start, non il metodo run.
public void contextInitialized(ServletContextEvent sce) {
//Some init code not relevant, omitted for clarity
BidPushThread t= new BidPushThread();
t.setServletContext(sce.getServletContext());
t.start();// run();
}
Altri suggerimenti
SINTESI:
- Contrassegna questo thread come thread daemon o thread utente. Il Java Virtual Machine esce quando solo i thread in esecuzione sono tutti daemon filettature.
- Questo metodo deve essere chiamato prima di avviare il thread.
- Questo metodo prima chiama il metodo checkAccess di questo thread
senza argomenti. Ciò può comportare lanciare SecurityException (nella
thread corrente).
SINTESI: in molti casi, ciò che realmente voglio è creare thread in background che svolgono compiti semplici e periodici in un applicazione. Il metodo setDaemon () può essere usato per contrassegnare un thread come a thread demone che dovrebbe essere ucciso e scartato quando nessun altro i thread dell'applicazione rimangono. Normalmente, l'interprete Java continua a funzionare fino al completamento di tutti i thread. Ma quando i thread daemon sono gli unici fili ancora vivi, l'interprete uscirà.
Questa sarebbe una pessima idea. Provocherebbe un carico della CPU del 100% senza una buona ragione.
La soluzione corretta è probabilmente quella di bloccare il thread quando la coda è vuota. Questo è banalmente implementato con un BlockingQueue
.
Can't I have a thread which is always running? When the app is removed,
that thread is stopped by the corresponding event in my ServletContextListener.
" Quella discussione è stata fermata " ;? Come? Non ci sono condizioni di terminazione nel tuo ciclo while (true) {...}. Come lo stai fermando? Stai usando il metodo Thread.stop ()? Non è sicuro ed è stato deprecato molto tempo fa in Java 1.1
Se si utilizza setDaemon (true), il thread rimarrà attivo dopo aver arrestato l'app Web utilizzando gli strumenti di gestione del proprio server delle app. Quindi, se riavvii l'app Web, otterrai un altro thread. Anche se si tenta di annullare la distribuzione dell'app Web, il thread rimarrà in esecuzione e impedirà che l'intera app Web venga raccolta in modo inutile. Quindi la ridistribuzione della versione successiva ti darà una copia aggiuntiva di tutto ciò che è in memoria.
Se fornisci una condizione di uscita per il loop (ad es. InterruptedException o un valore booleano "stopNow" volatile), puoi evitare questo problema.
Dai un'occhiata al metodo java.langThread.setDaemon (), può essere quello che ti serve