Uzyskaj listę wszystkich wątków aktualnie uruchomionych w Javie

232

Czy jest jakiś sposób, aby uzyskać listę wszystkich działających wątków w bieżącej maszynie JVM (w tym wątki, które nie zostały uruchomione przez moją klasę)?

Czy możliwe jest również uzyskanie Threadi Classobiektów wszystkich wątków na liście?

Chcę móc to zrobić za pomocą kodu.

Kryten
źródło

Odpowiedzi:

325

Aby uzyskać iterowalny zestaw:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
thejoshwolfe
źródło
19
Chociaż znacznie czystszy niż inna proponowana alternatywa, ma to jednak wadę, że wiąże się z kosztem uzyskania śladów stosu dla wszystkich wątków. Jeśli i tak będziesz używać tych śladów stosu, jest to wyraźnie lepsza opcja. Jeśli nie, może to być znacznie wolniejsze bez żadnego innego zysku niż czysty kod.
Eddie,
29
@Eddie Czy to założenie ze zdrowego rozsądku, czy przeprowadzałeś eksperymenty? mówisz „znacznie wolniej”; o ile wolniej? Czy warto? Podważam każdą próbę pogorszenia kodu ze względu na wydajność. Jeśli masz wymagania dotyczące wydajności i infrastrukturę do mierzenia wydajności ilościowo, to jestem w porządku z ludźmi, którzy pogarszają kod, ponieważ zdają się wiedzieć, co robią. Zobacz źródło wszelkiego zła według Donalda Knutha.
thejoshwolfe
20
Nie określiłem tych konkretnych alternatyw, ale pracowałem z innymi metodami Java zbierania śladów stosu zamiast tylko listy wątków. Wydaje się, że wpływ na wydajność zależy bardzo mocno od używanej maszyny JVM (na przykład JRockit vs. Sun JVM). Warto zmierzyć w konkretnym przypadku. To, czy wpłynie to na ciebie, zależy od twojego wyboru JVM i od liczby posiadanych wątków. Przekonałem się, że pobieranie wszystkich śladów stosu za pośrednictwem ThreadMXBean.dumpAllThreads dla około 250 wątków zajmuje 150-200 ms, a sama lista wątków (bez śladów) nie jest mierzalna (0 ms).
Eddie
4
W moim systemie (Oracle Java 1.7 VM) szybkie sprawdzenie pokazuje, że ta metoda jest ~ 70..80 razy wolniejsza niż alternatywna poniżej. Ślady stosu i odbicie należą do najcięższych operacji Java.
Franz D.
5
@thejoshwolfe: Oczywiście, czytelność jest ważnym czynnikiem i nie należy mikrooptymalizować itp. Jednak zrobiłem badania podczas pisania małego monitora wydajności aplikacji. W przypadku tego rodzaju narzędzia minimalny wpływ na wydajność jest niezbędny do uzyskania wiarygodnych danych, dlatego wybrałem metodę bez stosu.
Franz D.
75

Uzyskaj uchwyt do katalogu głównego ThreadGroup, w ten sposób:

ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parentGroup;
while ((parentGroup = rootGroup.getParent()) != null) {
    rootGroup = parentGroup;
}

Teraz enumerate()kilkakrotnie wywoływaj funkcję w grupie głównej. Drugi argument pozwala uzyskać rekurencyjnie wszystkie wątki:

Thread[] threads = new Thread[rootGroup.activeCount()];
while (rootGroup.enumerate(threads, true ) == threads.length) {
    threads = new Thread[threads.length * 2];
}

Zwróć uwagę, jak wywołujemy enumerate (), aż tablica będzie wystarczająco duża, aby pomieścić wszystkie wpisy.

Frerich Raabe
źródło
22
Jestem zszokowany, że ta strategia jest tak popularna w Internecie. Moja strategia jest znacznie prostsza (1 linia kodu) i działa równie dobrze z dodatkową zaletą unikania warunków wyścigu.
thejoshwolfe
11
@thejoshwolfe: Właściwie zgadzam się - myślę, że twoja odpowiedź jest znacznie lepsza i prawdopodobnie byłaby to odpowiedź zaakceptowana, gdyby nie spóźnił się o rok. Jeśli OP nadal będzie się zgadzać z SO, co najwyraźniej tak robi, dobrze byłoby odrzucić moją odpowiedź i raczej przyjąć twoją.
Frerich Raabe
2
Pamiętaj, że do wszystkiego innego niż rootGrouppowinieneś użyć new Thread[rootGroup.activeCount()+1]. activeCount()może wynosić zero, a jeśli tak, to wpadniesz w nieskończoną pętlę.
jmiserez
7
@ theheoshoshwolfe Podejrzewam, że to rozwiązanie jest znacznie tańsze.
Haozhun
19
+1 za tę niedocenianą odpowiedź, ponieważ jest ona bardziej odpowiednia do celów monitorowania IMHO. Jego naturalne warunki wyścigowe nie mają większego znaczenia w monitorowaniu. Jednak, jak wykazały niektóre szybkie testy „około trzydzieści”, jest to około 70–80 razy szybsze niż rozwiązanie oparte na stacktrace. Do monitorowania niezbędny jest niewielki odcisk wydajności, ponieważ chcesz zachować możliwie jak najmniejszy wpływ na monitorowany system (Heisenberg uderza ponownie :) W przypadku debugowania, gdzie możesz potrzebować bardziej wiarygodnych informacji, można zastosować metodę stacktrace kluczowy. BTW, rozwiązanie MxBean jest nawet wolniejsze niż przy użyciu stacktraces.
Franz D.
29

Tak, spójrz na listę wątków . Wiele przykładów na tej stronie.

To zrobić to programowo. Jeśli chcesz tylko listę w systemie Linux, możesz po prostu użyć tego polecenia:

kill -3 processid

a maszyna wirtualna wykona zrzut wątku na standardowe wyjście.

Cletus
źródło
5
zabić -3? Przynajmniej na moim Linuksie to „terminal quit”. Zabija, nie wymienia.
Michael H.
5
cletus jest rzeczywiście poprawny - kill -3 spowoduje zrzucenie wątku na standardowe wyjście, niezależnie od tego, co ten sygnał ma znaczyć. Zastanowiłbym się nad użyciem jstacka.
Dan Hardiker,
nie można uzyskać listy wątków : nadeausoftware.com odmówiło połączenia.
DSlomer64
14

Spojrzałeś na jconsole ?

Spowoduje to wyświetlenie listy wszystkich wątków uruchomionych dla określonego procesu Java.

Możesz uruchomić jconsole z folderu bin JDK.

Możesz także uzyskać pełny stos śledzenia dla wszystkich wątków, naciskając Ctrl+Breakw systemie Windows lub wysyłając kill pid --QUITw systemie Linux.

pjp
źródło
Chcę uzyskać dostęp do listy w mojej klasie java
Kryten
W takim przypadku spójrz na odpowiedź Cletusa.
pjp
3
Dlaczego ludzie głosują na to, kiedy facet powiedział, że chce programowego rozwiązania?
cletus
Ponieważ pytanie tego nie mówi. Przeredaguję pytanie, aby było wyraźne.
pjp
8

Użytkownicy Apache Commons mogą korzystać ThreadUtils. W bieżącej implementacji zastosowano wcześniej opisane podejście „spacer po grupie wątków”.

for (Thread t : ThreadUtils.getAllThreads()) {
      System.out.println(t.getName() + ", " + t.isDaemon());
}
gerardw
źródło
7

Możesz spróbować czegoś takiego:

Thread.getAllStackTraces().keySet().forEach((t) -> System.out.println(t.getName() + "\nIs Daemon " + t.isDaemon() + "\nIs Alive " + t.isAlive()));

i możesz oczywiście uzyskać więcej cech wątku, jeśli potrzebujesz.

hasskell
źródło
5

W Groovy możesz wywoływać metody prywatne

// Get a snapshot of the list of all threads 
Thread[] threads = Thread.getThreads()

W Javie można wywołać tę metodę za pomocą odbicia, pod warunkiem, że zezwala na to menedżer bezpieczeństwa.

Jarek Przygódzki
źródło
Dostaję błąd, getThreads nie zdefiniowane dla wątku. I nie widzę tej funkcji w dokumentacji.
4

Fragment kodu, aby uzyskać listę wątków rozpoczynanych od głównego wątku:

import java.util.Set;

public class ThreadSet {
    public static void main(String args[]) throws Exception{
        Thread.currentThread().setName("ThreadSet");
        for ( int i=0; i< 3; i++){
            Thread t = new Thread(new MyThread());
            t.setName("MyThread:"+i);
            t.start();
        }
        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
        for ( Thread t : threadSet){
            if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup()){
                System.out.println("Thread :"+t+":"+"state:"+t.getState());
            }
        }
    }
}

class MyThread implements Runnable{
    public void run(){
        try{
            Thread.sleep(5000);
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

wynik:

Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE

Jeśli potrzebujesz wszystkich wątków, w tym wątków systemowych, które nie zostały uruchomione przez program, usuń poniższy warunek.

if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup())

Teraz wyjście:

Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[Reference Handler,10,system]:state:WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[Finalizer,8,system]:state:WAITING
Thread :Thread[Signal Dispatcher,9,system]:state:RUNNABLE
Thread :Thread[Attach Listener,5,system]:state:RUNNABLE
Ravindra babu
źródło
3
    public static void main(String[] args) {


        // Walk up all the way to the root thread group
        ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
        ThreadGroup parent;
        while ((parent = rootGroup.getParent()) != null) {
            rootGroup = parent;
        }

        listThreads(rootGroup, "");
    }


    // List all threads and recursively list all subgroup
    public static void listThreads(ThreadGroup group, String indent) {
        System.out.println(indent + "Group[" + group.getName() + 
                ":" + group.getClass()+"]");
        int nt = group.activeCount();
        Thread[] threads = new Thread[nt*2 + 10]; //nt is not accurate
        nt = group.enumerate(threads, false);

        // List every thread in the group
        for (int i=0; i<nt; i++) {
            Thread t = threads[i];
            System.out.println(indent + "  Thread[" + t.getName() 
                    + ":" + t.getClass() + "]");
        }

        // Recursively list all subgroups
        int ng = group.activeGroupCount();
        ThreadGroup[] groups = new ThreadGroup[ng*2 + 10];
        ng = group.enumerate(groups, false);

        for (int i=0; i<ng; i++) {
            listThreads(groups[i], indent + "  ");
        }
    }
ZZ Coder
źródło
3

W konsoli Java naciśnij Ctrl-Break . Spowoduje to wyświetlenie listy wszystkich wątków oraz niektórych informacji o stercie. To oczywiście nie da ci dostępu do obiektów. Ale i tak może być bardzo pomocne przy debugowaniu.

raoulsson
źródło
1

Aby uzyskać listę wątków i ich pełnych stanów za pomocą terminala, możesz użyć następującego polecenia:

jstack -l <PID>

Który PID jest identyfikatorem procesu uruchomionego na twoim komputerze. Aby uzyskać identyfikator procesu java, wystarczy uruchomić jpspolecenie.

Ponadto możesz analizować zrzut wątku wygenerowany przez jstack w TDA (Thread Dump Analyzer), taki jak narzędzie szybkiej analizy wątku lub spotify .

Amir Fo
źródło