2012-05-24 8 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; 
} 

Kiedy profilowane aplikację, zauważyłem, że zaplanowane wątki tworzone tą metodą są zawsze w „Running” zamiast „Oczekiwanie” stanu, zanim zostaną one wykonane. Jeśli usuniemy executorService.shutdown(), zrobi to, co chcę (tzn. Wątki pozostają w stanie oczekiwania, dopóki nie nadejdzie czas, aby je uruchomić). Jednak bez executorService.shutdown() wątki nonDaemon nigdy nie zostaną zebrane podczas wykonywania. Czy istnieje sposób, w jaki mogę zagwarantować, że wątki są zawsze w stanie oczekiwania przed wykonaniem? lub co drugi wymiana mogę użyć tej metody, aby upewnić się, że:Jak utrzymać wątek czeka w ScheduledExecutorService który został zamknięty

  • mogę dołączyć prefiks do nazw wątków, które działają w służbie executora (to skutecznie, co robi realizacja DefaultThreadFactory)
  • Non-demon wątki pobierają GC po wykonaniu.
  • utworzone Wątki pozostają w stanie oczekiwania, dopóki nie nadejdzie czas ich uruchomienia.

Odpowiedz

2

Wykreśliłem rozwiązanie: To shutdown(), które zmienia stan wszystkich wątków wątków oczekujących z "oczekujących" na "uruchomione". Zamiast wywoływać shutdown() natychmiast, teraz używam executorService, aby zaplanować wywołanie do własnego zamknięcia(). Dzięki temu oczekujące zadania pozostają w stanie oczekiwania tak długo, jak to możliwe - oszczędzając w ten sposób zasoby procesora.

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 Dobra robota. Zastanawiałem się nad zaproponowaniem tego rozwiązania jako rozwiązania, ale działałoby ono tylko wtedy, gdyby jedno zadanie zostało zgłoszone zgodnie z harmonogramem (bez innych rodzajów kontroli, takich jak liczba wykonywanych zadań), więc zrezygnowałem. –

0

Wprowadzono kod. To z zrzutu wątku w jvisualvm (nazwałem wątek "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 

Teraz, to jest od grepcode dla tego 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(); 

co prowadzi mnie do wniosku, że problem znajduje się w twoim kodzie testowym - zamykasz executora zbyt szybko i wątek znajduje się w dziwnym stanie. Nie powinno to stanowić problemu w kodzie produkcyjnym.

+0

Byłby to problem w produkcji, gdyby zamknął executor, podczas gdy zadania czekają na zaplanowanie. –

+0

@JohnVint Ta konkretna linia kodu zostanie wykonana (jak wyjaśnia komentarz) w rzadkim przypadku, gdy wątek wchodzi w stan 'STOP' zaraz po tym, jak została uruchomiona jego metoda' start' (to jest linia 943, nie pokazana tutaj) . Prawdopodobnie masz na myśli ogólny problem, a nie ten konkretny. –

+0

Topolink Stan 'STOP' jest ustawiony tylko wtedy, gdy ExecutorService jest' shutdownNow() 'stan Executora, gdy' shutdown() 'jest' SHUTDOWN' –

2

To, co widzisz, polega na tym, że ScheduledExecutor deleguje do prostego ThreadPoolExecutor w celu zamknięcia systemu. Podczas zamykania TPE wszystkie wątki będą wirowały w kolejce roboczej backing, dopóki nie będzie pusta. Cóż, funkcja ScheduledThreadPool używa DelayedQueue, która może nie być pusta, ale jeśli ją sondujesz, otrzymasz zerową wartość, ponieważ zadanie nie jest gotowe do zaplanowania. Będzie wirować i obracać, dopóki nie będzie gotowy

Jedyne co naprawdę możesz zrobić, to shutdownNow i wykonać zadania, które zwracają w inny sposób.

także powiedziano mi jest to rzeczywiście stałe w Java 7

+0

+1 cos pierwsze 2 zdania pomogły mi znaleźć rozwiązanie. – Kes115

3

Nić nie czeka, bo didn't wywołać metodę future.get(). Zrobiłem test jednostkowy, aby to udowodnić.

testowy nr 1 (bez zawijania future.get() metoda):

@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()); 
} 

a wyjście było:

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

testowy nr 2 (wywołanie future.get() metody):

@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()); 
} 

a wyjście było:

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 

Mam nadzieję, że ci to pomoże!

+0

Chyba że nie przeczytam OP, nie myślę, że odnosi się on do zgłaszanego wątku (wątku oczekującego na wykonanie zadania). Sądzę, że odnosi się do wątku ExecutorService, który ciągle działa. Zobacz zrzut wątku Marko Topolnika, aby zaobserwować różnice. –

+0

@JohnVint ma rację. – Kes115

Powiązane problemy