2013-01-11 10 views
7

Używam architektury Executors w Javie do tworzenia pul wątków dla aplikacji wielowątkowej, a ja mam pytanie dotyczące wydajności.Optymalny sposób tworzenia puli wątków o stałym rozmiarze w Javie przy użyciu usługi Executors

Mam aplikację, która może pracować w trybie rzeczywistym lub w czasie innym niż rzeczywisty. W przypadku, gdy jest to w czasie rzeczywistym, ja po prostu przy użyciu następujących:

THREAD_POOL = Executors.newCachedThreadPool(); 

ale w przypadku nie jest to w czasie rzeczywistym, chcę zdolność do kontrolowania wielkości mojego puli wątków. Aby to zrobić, myślę o 2 opcjach, ale tak naprawdę nie rozumiem różnicy i która z nich byłaby lepsza.

Wariant 1 jest użycie prosty sposób:

THREAD_POOL = Executors.newFixedThreadPool(threadPoolSize); 

Wariant 2 jest tworzyć własne ThreadPoolExecutor takiego:

RejectedExecutionHandler rejectHandler = new RejectedExecutionHandler() { 
@Override 
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 
    try { 
     executor.getQueue().put(r); 
    } catch (Exception e) {} 
} 
};   
THREAD_POOL = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10000), rejectHandler); 

Chciałbym zrozumieć, co jest zaletą przy użyciu bardziej złożoną opcję 2, a także gdybym użył innej struktury danych niż LinkedBlockingQueue? Każda pomoc będzie doceniona.

Odpowiedz

13

Patrząc na kod źródłowy zdasz sobie sprawę, że:

Executors.newFixedThreadPool(threadPoolSize); 

jest równoważne do:

return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, MILLISECONDS, 
           new LinkedBlockingQueue<Runnable>()); 

Ponieważ nie przewiduje wyraźny RejectedExecutionHandler, domyślnie AbortPolicy służy. Zasadniczo rzuca RejectedExecutionException, gdy kolejka jest pełna. Ale kolejka jest nieograniczona, więc nigdy nie będzie pełna. W związku z tym ten executor akceptuje liczbę zadań nieoznaczoną .

Twoja deklaracja jest o wiele bardziej skomplikowane i zupełnie inna:

  • new LinkedBlockingQueue<Runnable>(10000) spowoduje puli wątków, aby odrzucić zadania, jeśli więcej niż 10.000 czekają.

  • Nie rozumiem, co robi Twój RejectedExecutionHandler. Jeśli pula odkryje, że nie może umieścić więcej runnables w kolejce, wywoła twój program obsługi. W tej procedurze ... próbujesz ponownie umieścić tę Runnable w kolejce (co spowoduje, że zawodzi w 99% przypadków bloków). W końcu przełkniesz wyjątek. Wygląda na to, że chcesz mieć ThreadPoolExecutor.DiscardPolicy.

    Patrząc na twoje komentarze poniżej wydaje się, że próbujesz zablokować lub w jakiś sposób dusić klientów, jeśli kolejka zadań jest zbyt duża. Myślę, że blokowanie wewnątrz RejectedExecutionHandler nie jest dobrym pomysłem. Zamiast tego zastanów się nad polityką odrzucania. Nie do końca tak samo, ale wystarczająco blisko.

Zawijanie: jeśli chcesz ograniczyć liczbę oczekujących zadań, Twoje podejście jest prawie dobre. Jeśli chcesz ograniczyć liczbę współbieżnych wątków, wystarczy pierwszy pojedynczy liniowiec.

1 - zakładając, 2^31 nieskończoność

+0

'RejectedExecutionHandler' rzeczywiście blokuje, wywołanie' executor.getQueue() put (R); 'blokuje aż kolejka uwalnia się, tzw. w końcu mój handler pozwala zachować ograniczoną kolejkę bez przerywania jakiegokolwiek zadania. O ile się nie mylę. +1 dla innych szczegółów. –

+0

@CharlesMenguy: dziękuję za wyjaśnienia, moje złe, zaktualizuję moje pytanie. Ale co chcesz osiągnąć przez blokowanie wewnątrz 'RejectedExecutionHandler'? Wierzę, że może mieć naprawdę nieoczekiwane efekty uboczne, takie jak blokowanie wątku wywołującego. Może potrzebujesz 'CallerRunsPolicy'? –

+0

Właściwie po obejrzeniu tego, 'CallerRunsPolicy' brzmi naprawdę obiecująco dla tego, co chcę zrobić, spróbuję dzięki! Może możesz to dodać w odpowiedzi, a ja przyjmuję twoją odpowiedź. –

Powiązane problemy