รับรายการเธรดทั้งหมดที่กำลังทำงานใน Java


232

มีวิธีใดบ้างที่ฉันสามารถรับรายการเธรดที่กำลังรันทั้งหมดใน JVM ปัจจุบัน (รวมถึงเธรดที่ไม่ได้เริ่มโดยคลาสของฉัน)?

มันเป็นไปได้ที่จะได้รับThreadและClassวัตถุของกระทู้ทั้งหมดในรายการหรือไม่

ฉันต้องการที่จะสามารถทำสิ่งนี้ผ่านรหัส

คำตอบ:


325

ในการรับชุด iterable:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();

19
ในขณะที่สะอาดกว่าทางเลือกอื่น ๆ ที่เสนอมานี้มีข้อเสียของค่าใช้จ่ายในการรับการติดตามสแต็กสำหรับเธรดทั้งหมด หากคุณจะใช้การติดตามสแต็กเหล่านี้แสดงว่าดีกว่าอย่างชัดเจน ถ้าไม่เช่นนั้นอาจช้าลงอย่างมากหากไม่ได้รับรหัสอื่นที่ชัดเจน
Eddie

29
@Eddie นั่นคือข้อสมมติฐานจากสามัญสำนึกหรือคุณทำการทดลองหรือไม่? "ช้าลงอย่างมาก" คุณพูด ช้าเท่าไหร่ มันคุ้มหรือไม่ ฉันตั้งคำถามกับความพยายามใด ๆ ที่จะทำให้โค้ดแย่ลงเพื่อประสิทธิภาพ หากคุณมีข้อกำหนดด้านประสิทธิภาพและโครงสร้างพื้นฐานในการวัดประสิทธิภาพเชิงปริมาณฉันก็โอเคกับคนที่ทำโค้ดแย่กว่าเพราะพวกเขาดูเหมือนจะรู้ว่าพวกเขากำลังทำอะไรอยู่ ดูรากของความชั่วร้ายทั้งหมดตาม Donald Knuth
thejoshwolfe

20
ฉันยังไม่ได้กำหนดเวลาทางเลือกเฉพาะเหล่านี้ แต่ฉันได้ทำงานร่วมกับ Java วิธีอื่นในการรวบรวมร่องรอยสแต็กเทียบกับเพียงรายการของเธรด ผลกระทบต่อประสิทธิภาพดูเหมือนจะขึ้นอยู่กับ JVM ที่คุณใช้เป็นอย่างมาก (ตัวอย่างเช่น JRockit vs Sun JVM) มันคุ้มค่าในการวัดในกรณีเฉพาะของคุณ ไม่ว่าจะมีผลกระทบหรือไม่นั้นขึ้นอยู่กับตัวเลือก JVM ของคุณและขึ้นอยู่กับจำนวนกระทู้ที่คุณมี ฉันพบว่าการรับการติดตามสแต็กทั้งหมดผ่าน ThreadMXBean.dumpAllThreads ประมาณ 250 เธรดเพื่อรับ 150 - 200 msec ในขณะที่รับเพียงรายการของเธรด (ไม่มีร่องรอย) ไม่สามารถวัดได้ (0 msec)
Eddie

4
ในระบบของฉัน (Oracle Java 1.7 VM) การตรวจสอบอย่างรวดเร็วแสดงให้เห็นว่าวิธีนี้คือ ~ 70..80 เท่า SLOWER มากกว่าทางเลือกด้านล่าง ร่องรอยสแต็กและการสะท้อนกลับเป็นของการดำเนินการ Java ที่หนักที่สุด
Franz D.

5
@Thejoshwolfe: แน่นอนความสามารถในการอ่านเป็นปัจจัยสำคัญและไม่ควรปรับให้เหมาะกับขนาดเล็ก แต่อย่างใดอย่างไรก็ตามฉันทำวิจัยของฉันในขณะที่เขียนแอปพลิเคชันประสิทธิภาพขนาดเล็ก สำหรับเครื่องมือประเภทนี้จำเป็นต้องมีประสิทธิภาพน้อยที่สุดในการรับข้อมูลที่เชื่อถือได้
Franz D.

75

จัดการกับรูThreadGroupทแบบนี้:

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

ตอนนี้เรียกใช้enumerate()ฟังก์ชันในกลุ่มรากซ้ำ ๆ อาร์กิวเมนต์ที่สองช่วยให้คุณได้รับกระทู้ทั้งหมดซ้ำ:

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

สังเกตวิธีที่เราเรียกแจกแจง () ซ้ำ ๆ จนกระทั่งอาร์เรย์มีขนาดใหญ่พอที่จะมีรายการทั้งหมด


22
ฉันตกใจที่กลยุทธ์นี้เป็นที่นิยมในอินเทอร์เน็ต กลยุทธ์ของฉันนั้นเรียบง่ายกว่า (โค้ด 1 บรรทัด) และทำงานได้ดีพร้อมกับโบนัสเพิ่มเติมเพื่อหลีกเลี่ยงสภาพการแข่งขัน
thejoshwolfe

11
@thejoshwolfe: จริง ๆ แล้วฉันเห็นด้วย - ฉันคิดว่าคำตอบของคุณดีขึ้นมากและอาจเป็นคำตอบที่ได้รับการยอมรับในตอนแรกถ้ามันไม่ได้มาช้ากว่าหนึ่งปี หาก OP ยังคงมีความถี่มากดังนั้นซึ่งเห็นได้ชัดว่าเขาทำเขาควรได้รับคำแนะนำที่ดีในการยกเลิกคำตอบของฉันและยอมรับของคุณ
Frerich Raabe

2
โปรดทราบว่าสำหรับสิ่งอื่นที่ไม่ใช่คุณควรใช้rootGroup อาจเป็นศูนย์และถ้าเป็นคุณจะพบกับการวนซ้ำไม่สิ้นสุด new Thread[rootGroup.activeCount()+1]activeCount()
jmiserez

7
@Thejoshwolfe ฉันคิดว่าวิธีนี้มีราคาไม่แพงมาก
Haozhun

19
+1 สำหรับคำตอบที่มีการประเมินต่ำกว่านี้เนื่องจากเหมาะสมกว่าสำหรับวัตถุประสงค์ในการตรวจสอบ IMHO สภาพการแข่งขันโดยธรรมชาติของมันไม่สำคัญมากในการตรวจสอบ อย่างไรก็ตามตามที่มีการทดสอบอย่างรวดเร็วพบว่ามันเร็วกว่าการแก้ปัญหาโดยใช้ stacktrace ประมาณ 70-80 เท่า สำหรับการตรวจสอบจำเป็นต้องมีประสิทธิภาพเล็ก ๆ น้อย ๆ ตามที่คุณต้องการเก็บเอฟเฟกต์ในระบบที่ถูกตรวจสอบให้เล็กที่สุดเท่าที่จะทำได้ (ไฮเซนเบิร์กนัดอีกครั้ง :) สำหรับการดีบักที่คุณอาจต้องการข้อมูลที่เชื่อถือได้มากขึ้น สำคัญ BTW โซลูชั่น MxBean ช้ากว่าการใช้กองซ้อน
Franz D.

29

ใช่จะดูที่ได้รับรายชื่อของกระทู้ ตัวอย่างจำนวนมากในหน้านั้น

นั่นคือการทำโปรแกรม หากคุณต้องการรายการบน Linux อย่างน้อยคุณก็สามารถใช้คำสั่งนี้:

kill -3 processid

และ VM จะทำการเธรดดัมพ์ไปยัง stdout


5
ฆ่า -3 อย่างน้อยใน linux ของฉันนั่นคือ "terminal exit" การฆ่าไม่แสดง
Michael H.

5
cletus ถูกต้องแน่นอน - kill -3 จะเธรดดัมพ์ไปยัง stdout โดยไม่คำนึงถึงสัญญาณที่ควรจะหมายถึง ฉันจะพิจารณาใช้ jstack แทน
Dan Hardiker

ไม่สามารถเข้าถึงรายการหัวข้อได้: nadeausoftware.com ปฏิเสธที่จะเชื่อมต่อ
DSlomer64


14

คุณลองดูที่jconsoleบ้างไหม?

นี่จะแสดงรายการเธรดทั้งหมดที่ทำงานสำหรับกระบวนการ Java เฉพาะ

คุณสามารถเริ่ม jconsole จากโฟลเดอร์ JDK bin

คุณยังสามารถรับการติดตามแบบเต็มสแต็กสำหรับเธรดทั้งหมดโดยกดปุ่มCtrl+Breakใน Windows หรือโดยการส่งkill pid --QUITใน Linux


ฉันต้องการที่จะเข้าถึงรายชื่อภายในคลาสจาวาของฉัน
Kryten

ในกรณีนี้ให้ดูคำตอบของ cletus
pjp

3
อืมทำไมผู้คนถึงลงคะแนนในเรื่องนี้เมื่อผู้ชายพูดว่าเขาต้องการวิธีแก้ปัญหาเชิงโปรแกรม
cletus

เพราะคำถามไม่ได้ระบุว่า ฉันจะแก้ไขคำถามเพื่อให้ชัดเจน
pjp

8

ผู้ใช้ Apache Commons สามารถใช้งานThreadUtilsได้ การนำไปใช้ในปัจจุบันใช้วิธีการเดินตามกลุ่มเธรดที่อธิบายไว้ก่อนหน้านี้

for (Thread t : ThreadUtils.getAllThreads()) {
      System.out.println(t.getName() + ", " + t.isDaemon());
}

7

คุณสามารถลองสิ่งนี้:

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

และแน่นอนคุณสามารถรับลักษณะเธรดเพิ่มเติมถ้าคุณต้องการ


5

ในGroovyคุณสามารถเรียกวิธีการส่วนตัวได้

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

ในJavaคุณสามารถเรียกใช้เมธอดดังกล่าวโดยใช้การสะท้อนหากผู้จัดการความปลอดภัยอนุญาต


ฉันได้รับข้อผิดพลาดไม่ได้กำหนดเธรดสำหรับเธรด และฉันไม่เห็นฟังก์ชั่นนี้ในเอกสารประกอบ

4

ข้อมูลโค้ดเพื่อรับรายการเธรดที่เริ่มโดยเธรดหลัก:

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

เอาท์พุท:

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

หากคุณต้องการกระทู้ทั้งหมดรวมถึงกระทู้ระบบซึ่งโปรแกรมของคุณยังไม่ได้เริ่มให้ลบเงื่อนไขด้านล่าง

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

ตอนนี้ออก:

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

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 + "  ");
        }
    }

3

ในคอนโซล Java, กดCtrl-หยุด มันจะแสดงรายการกระทู้ทั้งหมดรวมถึงข้อมูลบางอย่างเกี่ยวกับกอง สิ่งนี้จะไม่ทำให้คุณสามารถเข้าถึงวัตถุได้แน่นอน แต่มันจะมีประโยชน์มากสำหรับการแก้ไขข้อบกพร่องต่อไป


1

ในการรับรายการเธรดและสถานะเต็มโดยใช้เทอร์มินัลคุณสามารถใช้คำสั่งด้านล่าง:

jstack -l <PID>

PID ใดเป็นรหัสของกระบวนการที่ทำงานบนคอมพิวเตอร์ของคุณ เพื่อรับรหัสกระบวนการของกระบวนการ Java ของคุณคุณสามารถเรียกใช้jpsคำสั่ง

นอกจากนี้คุณยังสามารถวิเคราะห์การถ่ายโอนข้อมูลหัวข้อของคุณที่ผลิตโดย jstack ใน TDAs (การถ่ายโอนข้อมูลกระทู้วิเคราะห์) เช่นfastthreadหรือเครื่องมือวิเคราะห์ด้าย Spotify


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.