2012-01-26 12 views
14

Próbuję użyć zarówno InheritableThreadLocal i ThreadPoolExecutor.Korzystanie z InheritableThreadLocal z ThreadPoolExecutor - lub - ThreadPoolExecutor, który nie używa ponownie wątków

Dzieli się, ponieważ ThreadPoolExecutor ponownie wykorzystuje wątki dla każdej puli (w końcu jest to pula), co oznacza, że ​​InheritableThreadLocal nie działa zgodnie z oczekiwaniami. Teraz problem wydaje mi się oczywisty, ale szczególnie trudna do wyśledzenia.

Używam InheritableThreadLocal, więc każdy z kilku procesów najwyższego poziomu ma własne połączenie z bazą danych dla siebie i wszelkich podprocesów, które spawns. Nie używam tylko jednej wspólnej puli połączeń, ponieważ każdy proces na najwyższym poziomie będzie wykonywał wiele wieloetapowych prac związanych z połączeniem przed przystąpieniem do bazy danych i/lub przygotowaniem wielu gotowych zestawów, które są używane w kółko.

Używam wspólnego ThreadPoolExecutor między tymi procesami najwyższego poziomu, ponieważ istnieją pewne zachowania, które muszą być bramkowane. na przykład Mimo że mogę uruchomić 4 procesy najwyższego poziomu, mogę mieć tylko jeden proces zapisujący dane do bazy danych na raz (lub system musi przejść do innego udostępnionego zasobu). Tak więc będę miał proces najwyższego poziomu, który utworzy Runnable i wyśle ​​go do współużytkowanego ThreadPoolExecutor, aby upewnić się, że nie więcej niż jeden (lub dwa lub trzy w zależności od przypadku) są uruchomione w tym samym czasie w całym systemie.

Problem polega na tym, że ponieważ ThreadPoolExecutor ponownie używa wątków dla pul, InheritableThreadLocal pobiera początkową wartość, która została uruchomiona w tej puli, zamiast wartości, która była w procesie najwyższego poziomu, który wysłał Runnable do ThreadPoolExecutor.

  • Czy istnieje jakiś sposób, aby wymusić na basen pracownikiem w ThreadPoolExecutor użyć wartości InheritableThreadLocal że był w kontekście procesu, które utworzyły Runnable zamiast w kontekście ponownego wykorzystania puli wątków?

  • Czy istnieje implementacja ThreadPoolExecutor, która tworzy nowy wątek za każdym razem, gdy rozpoczyna nowy program Runnable? Dla moich celów zależy mi tylko na bramkowaniu liczby jednocześnie działających wątków do ustalonego rozmiaru.

  • Czy istnieją inne rozwiązania lub sugestie, które ludzie mają dla mnie, aby osiągnąć to, co opisałem powyżej?

(Choć zdaję sobie sprawę, mogę rozwiązać ten problem przez przepuszczenie całego połączenia z bazą danych z klasy do klasy podwątku do podwątku jak pewnego rodzaju wspólnoty rowerze, chciałbym tego uniknąć).

Istnieje również poprzednie pytanie dotyczące StackOverflow, InheritableThreadLocal and thread pools, które rozwiązuje również ten problem. Jednak rozwiązanie tego problemu wydaje się, że jest to kiepski przypadek użycia dla InheritableThreadLocal, który moim zdaniem nie dotyczy mojej sytuacji.

Dzięki za wszelkie pomysły.

Odpowiedz

5

Zamiast używać ThreadPoolExecutor do ochrony udostępnionych zasobów, może warto skorzystać z java.util.concurrent.Semaphore? Zadania podrzędne, które utworzysz, będą działać do końca we własnych wątkach, ale dopiero po uzyskaniu pozwolenia z semafora i oczywiście zwolnieniu pozwolenia po zakończeniu.

+0

Nie użyłem Semaforów, więc przyjrzę się temu. Używamy wielu innych funkcji Executorów (Futures, timeout itp.), Ale jest to możliwe, że mogę to zrobić z Semaforami lub że mogę użyć zwykłego executora (nie puli wątków) oraz semaforów, aby uzyskać wszystko, czego potrzebuję. Dzięki. –

+0

To była właściwa droga. Używanie Semaphores i FutureTasks było bardziej reprezentatywnym paradygmatem tego, co chcieliśmy zrobić. Po zmianie kod został znacznie uproszczony, co zawsze jest dobrym znakiem. Dodatkowo rozwiązało wszelkie problemy z InheritableThreadLocal. –

0

Wcześniej mieliśmy ten sam problem i rozwiązaliśmy ten problem, pisząc ThreadLocalContextMigrator, który zasadniczo kopiuje kontekst lokalny wątku do zadania, które zostanie wykonane przy użyciu wątku z puli. Zadanie, podczas wykonywania, zbierze więcej informacji kontekstowych, a po zakończeniu zadania skopiujemy je.

+0

Czy możesz podać trochę więcej szczegółów na temat tego, jak to zrobiłeś? –

+0

Czy "ThreadLocalContextMigrator.java" jest gdzieś publicznie dostępne? –

3

używanie InheritedThreadLocal jest prawie na pewno nie tak. Prawdopodobnie nie zadałbyś pytania, czy możesz dopasować to dziwaczne narzędzie. Przede wszystkim jest strasznie podatna na przecieki i często wartość (wartości) ucieka w niektórych dziwnych wątkach.

Co do opcji Runnable is associate w/a context. Zastąp publiczną void execute(Runnable command) z ExecutorPool i zawiń Runnable w jakimś kontekście niosącym żądaną wartość od InheritedThreadLocal.

Klasa owijania powinien wyglądać mniej więcej tak

class WrappedRunnable extends Runnable{ 
    static final ThreadLocal<Ctx> context=new ThreadLocal<Ctx>(); 
    final Runnable target; 
    final Ctx context; 
    WrappedRunnable(Ctx context, Runnable target){...} 

    public void run(){ 
    ctx.set(context); 
    try{ 
     target.run(); 
    }finally{ 
     ctx.set(null);//or ctx.remove() 
    } 
    } 
} 

Alternatywnie, istnieje jakikolwiek realizacja ThreadPoolExecutor że tworzy nowy> wątku każdym uruchomieniu nowego runnable? Dla moich celów zależy mi tylko na bramkowaniu> liczby jednocześnie działających wątków do ustalonego rozmiaru.

Choć naprawdę złe z punktu widzenia wydajności, można zaimplementować własną rękę, w zasadzie trzeba tylko execute(Runnable task) metodę Executor że przyczynia się do zaostrzenia nowy wątek i zaczyna go.

+0

Co sprawia, że ​​mówisz, że InheritableThreadLocal jest podatny na wycieki? Googlam o to i nie widzę żadnych artykułów ani przykładów miejsc, w których powoduje to wycieki pamięci. Czy możesz wskazać mi jakieś źródła? –

+0

@JeffGoldberg, dobrze wiem, jak to jest zaimplementowane i mam dużo infrastruktury/middleware. ThreadLocal jest podatny na wycieki, a odziedziczona wersja jest bardzo podatna na wycieki. Praktycznie nie można kontrolować, kiedy nowy wątek jest tworzony z dowolnego powodu (sam JDK to robi). Długotrwały wątek wycieknie z wartości i swojego modułu ładującego klasy i nie ma możliwości zatrzymania tak dalekiego wątku od zatrzymania odniesienia do bezużytecznego obiektu. "childValue" może naprawić sprawę, ale jest wywoływana w wątku, który ją zawiera, a nowy docelowy wątek jest nieznany ... – bestsss

+0

Krótka notatka: To podejście zadziałało. Skończyło się na tym, że skorzystałem z porady od czterdziestu dwóch i zamiast tego zmieniłem podejście, inaczej wybrałbym to jako odpowiedź. Dzięki za radę i pomoc. –

0

Dlaczego po prostu nie przekazać bieżącego połączenia do żadnych pod zadań powierzonych przez główne zadanie? może jakiś współdzielony obiekt Context?