2012-05-24 3 views
5
public ScheduledFuture<?> executeTaskWithDelay(String name, 
     final Runnable runnable, Period delay, boolean isDaemon) { 
    ScheduledExecutorService executorService = 
     Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
      name, isDaemon)); 
    ScheduledFuture<?> future = executorService.schedule(runnable, 
     delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS); 
    executorService.shutdown(); 
    return future; 
} 

Quando ho eseguito il profiling dell'applicazione, ho notato che i thread pianificati creati con questo metodo sono sempre in "In esecuzione" anziché in stato "In attesa" prima di essere eseguiti. Se rimuovo executorService.shutdown() fa ciò che voglio (cioè i thread rimangono in attesa fino a quando non è il momento di eseguirli). Tuttavia, senza executorService.shutdown(), i thread non Demone non vengono mai raccolti automaticamente dopo l'esecuzione. C'è un modo per garantire che i thread siano sempre in attesa prima dell'esecuzione? o quale altra sostituzione posso usare per questo metodo per garantire che:Come mantenere thread in attesa in ScheduledExecutorService che è stato arrestato

  • posso collegare prefisso ai nomi di fili che corrono nel servizio esecutore (che è efficace quanto l'attuazione DefaultThreadFactory fa)
  • non-daemon i thread ottengono GC dopo l'esecuzione.
  • creato I thread rimangono nello stato di attesa finché non è il momento di eseguirli.

risposta

2

Calcolato una soluzione: È shutdown() che modifica lo stato di tutti i thread di attività in sospeso da "in attesa" a "in esecuzione". Anziché chiamare shutdown() immediatamente, ora utilizzo executorService per pianificare la chiamata al proprio shutdown(). Ciò garantisce che i task in sospeso restino nello stato di attesa il più a lungo possibile, risparmiando così risorse CPU.

public ScheduledFuture<?> executeTaskWithDelay(String name, 
     final Runnable runnable, Period delay, boolean isDaemon) { 
    final ScheduledExecutorService executorService = 
     Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
     name, isDaemon)); 
    ScheduledFuture<?> future = executorService.schedule(runnable, 
     delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS); 

    executorService.schedule(new Runnable() { 
     @Override 
     public void run() { 
     executorService.shutdown(); 
     }}, delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS); 

    return future; 
} 
+0

+1 Ben fatto. Stavo pensando di suggerire questa soluzione come soluzione, ma funzionerebbe solo se un'attività è stata sottoposta a pianificazione (senza altri tipi di controlli come il conteggio delle esecuzioni), quindi ho deciso di non farlo. –

0

Esegui il tuo codice. Questo è dalla discarica filo in jvisualvm (ho chiamato il filo "bla"):

"bla" prio=5 tid=7fa2bc16a000 nid=0x10bd53000 runnable [10bd52000] 
    java.lang.Thread.State: RUNNABLE 
    at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:950) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907) 
    at java.lang.Thread.run(Thread.java:680) 

    Locked ownable synchronizers: 
    - None 

Ora, questo è da grepcode per quella ThreadPoolExecutor.java:950:

944   // It is possible (but unlikely) for a thread to have been 
945   // added to workers, but not yet started, during transition to 
946   // STOP, which could result in a rare missed interrupt, 
947   // because Thread.interrupt is not guaranteed to have any effect 
948   // on a non-yet-started Thread (see Thread#interrupt). 
949   if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted()) 
950    t.interrupt(); 

Il che mi porta a concludere che il problema è nel tuo codice di test - stai spegnendo l'esecutore troppo velocemente e il thread si trova in uno stato strano. Questo non dovrebbe essere un problema nel codice di produzione.

+0

Questo sarebbe un problema in produzione se si spegne l'esecutore mentre tutte le attività sono in attesa di essere programmate. –

+0

@JohnVint Questa particolare riga di codice verrà eseguita solo (come spiega il commento) nel raro caso in cui un thread entri nello stato 'STOP' immediatamente dopo che è stato eseguito il suo metodo' start' (cioè la riga 943, non mostrata qui) . Probabilmente intendi il problema generale, non quello specifico. –

+0

Topolink Lo stato 'STOP' è impostato solo quando ExecutorService è' shutdownNow() 'lo stato dell'Executor quando' shutdown() 'è' SHUTDOWN' –

2

Quello che viene visualizzato è ScheduledExecutor che delega al programma ThreadPoolExecutor per la funzionalità di arresto. Quando si spegne il TPE, tutti i suoi fili girano sulla coda di lavoro di supporto finché non è vuota. Bene un ScheduledThreadPool utilizza un DelayedQueue che potrebbe non essere vuoto ma se lo hai interrogato otterresti un null indietro perché l'attività non è pronta per la pianificazione. Girerà e girerà fino a quando non sarà pronto

L'unica cosa che si può veramente fare è shutdownOra ed eseguire le attività che ritorna in un altro modo.

Inoltre mi è stato detto questo è in realtà fissato in Java 7

+0

+1 perché le prime 2 frasi mi hanno aiutato a capire una soluzione. – Kes115

3

Il filo non è in attesa, perché lei non si chiama il metodo future.get(). Ho fatto un test unitario per provarlo.

Test # 1 (senza chiamare future.get metodo()):

@Test 
public void testSchedule() throws InterruptedException, ExecutionException { 

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); 
    System.out.println(new Date()); 
    ScheduledFuture<?> future = executorService.schedule(new Runnable() { 

     public void run() { 
      System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");     
     } 

    }, 5, TimeUnit.SECONDS); 

    //System.out.println("future : " + future.get()); 

    executorService.shutdown(); 
    System.out.println(new Date()); 
} 

e la produzione ammontava:

Thu May 24 10:11:14 BRT 2012 
Thu May 24 10:11:14 BRT 2012 

Test # 2 (chiamando future.get() metodo):

@Test 
public void testSchedule() throws InterruptedException, ExecutionException { 

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); 
    System.out.println(new Date()); 
    ScheduledFuture<?> future = executorService.schedule(new Runnable() { 

     public void run() { 
      System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");     
     } 

    }, 5, TimeUnit.SECONDS); 

    System.out.println("future : " + future.get()); 

    executorService.shutdown(); 
    System.out.println(new Date()); 
} 

E la produzione è stata:

Thu May 24 10:12:48 BRT 2012 
8 - pool-1-thread-1 - Executing thread... 
future : null 
Thu May 24 10:12:53 BRT 2012 

Spero che ti aiuti!

+0

A meno che non abbia letto l'OP non credo che si stia riferendo al thread di presentazione (il thread in attesa del completamento del task). Credo che si stia riferendo al thread di ExecutorService che è continuamente in esecuzione. Vedi la discarica di Marko Topolnik per osservare le differenze. –

+0

@JohnVint ha ragione. – Kes115