วิธีหยุดกระบวนการ java อย่างสง่างาม?


88

ฉันจะหยุดกระบวนการ Java อย่างสง่างามใน Linux และ Windows ได้อย่างไร

Runtime.getRuntime().addShutdownHookเรียกเมื่อใดและไม่ได้รับเมื่อใด

แล้วเข้ารอบสุดท้ายพวกเขาช่วยอะไรที่นี่?

ฉันสามารถส่งสัญญาณบางประเภทไปยังกระบวนการ Java จากเชลล์ได้หรือไม่

ฉันกำลังมองหาโซลูชันแบบพกพาที่ดีกว่า


คุณควรกำหนดสิ่งที่คุณหมายถึงอย่างสง่างาม (โปรด)
Henry B

7
ฉันคิดว่าพวกเขาหมายความว่าพวกเขาต้องการได้รับโอกาสในการล้างทรัพยากรปล่อยล็อกและล้างข้อมูลถาวรใด ๆ ลงในดิสก์ก่อนที่โปรแกรมจะถูกฆ่า
Steve g

คำตอบ:


82

Shutdown hooks ดำเนินการในทุกกรณีที่ VM ไม่ได้ถูกบังคับให้ฆ่า ดังนั้นหากคุณต้องออก "มาตรฐาน" kill ( SIGTERMจากคำสั่ง kill) พวกเขาก็จะดำเนินการ System.exit(int)ในทำนองเดียวกันพวกเขาจะดำเนินการหลังจากเรียก

อย่างไรก็ตามการฆ่ายาก ( kill -9หรือkill -SIGKILL) พวกเขาจะไม่ดำเนินการ ในทำนองเดียวกัน (และเห็นได้ชัด) พวกเขาจะไม่ทำงานหากคุณดึงพลังงานจากคอมพิวเตอร์ทิ้งลงในถังลาวาที่เดือดหรือทุบ CPU เป็นชิ้น ๆ ด้วยค้อนขนาดใหญ่ คุณอาจรู้อยู่แล้วว่า

Finalizers ควรทำงานด้วยเช่นกัน แต่ไม่ควรพึ่งพาสิ่งนั้นในการล้างข้อมูลการปิดระบบ แต่ควรพึ่งพาการปิดระบบของคุณเพื่อหยุดสิ่งต่างๆอย่างหมดจด และเช่นเคยระวังการหยุดชะงัก (ฉันเห็นตะขอปิดเครื่องมากเกินไปทำให้กระบวนการทั้งหมดค้าง)!


1
น่าเสียดายที่ดูเหมือนว่าจะใช้ไม่ได้กับ Windows 7 (64 บิต) ฉันได้ลองใช้ taskill โดยไม่มีแฟล็กบังคับและพบข้อผิดพลาดต่อไปนี้: "ข้อผิดพลาด: ไม่สามารถยุติกระบวนการที่มี PID 14324 ได้เหตุผล: กระบวนการนี้สามารถยุติได้โดยบังคับเท่านั้น (ด้วยตัวเลือก / F)" การระบุตัวเลือกการบังคับ "/ f" จะปิดกระบวนการทันที
Jason Huntley

คุณไม่สามารถพึ่งพาโปรแกรมสุดท้ายที่กำลังเรียกใช้
Thorbjørn Ravn Andersen

47

ตกลงหลังจากความเป็นไปได้ทั้งหมดฉันได้เลือกที่จะทำงานกับ "Java Monitoring and Management"
ภาพรวมอยู่ที่นี่
ซึ่งช่วยให้คุณสามารถควบคุมแอปพลิเคชันหนึ่งจากอีกแอปหนึ่งได้อย่างง่ายดาย คุณสามารถเรียกใช้แอปพลิเคชันควบคุมจากสคริปต์เพื่อหยุดแอปพลิเคชันที่ควบคุมอย่างนิ่มนวลก่อนที่จะฆ่ามัน

นี่คือรหัสแบบง่าย:

แอปพลิเคชันที่ควบคุม:
รันด้วยพารามิเตอร์ VM folowing:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management jmxremote.ssl = เท็จ

//ThreadMonitorMBean.java
public interface ThreadMonitorMBean
{
String getName();
void start();
void stop();
boolean isRunning();
}

// ThreadMonitor.java
public class ThreadMonitor implements ThreadMonitorMBean
{
private Thread m_thrd = null;

public ThreadMonitor(Thread thrd)
{
    m_thrd = thrd;
}

@Override
public String getName()
{
    return "JMX Controlled App";
}

@Override
public void start()
{
    // TODO: start application here
    System.out.println("remote start called");
}

@Override
public void stop()
{
    // TODO: stop application here
    System.out.println("remote stop called");

    m_thrd.interrupt();
}

public boolean isRunning()
{
    return Thread.currentThread().isAlive();
}

public static void main(String[] args)
{
    try
    {
        System.out.println("JMX started");

        ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread());

        MBeanServer server = ManagementFactory.getPlatformMBeanServer();

        ObjectName name = new ObjectName("com.example:type=ThreadMonitor");

        server.registerMBean(monitor, name);

        while(!Thread.interrupted())
        {
            // loop until interrupted
            System.out.println(".");
            try 
            {
                Thread.sleep(1000);
            } 
            catch(InterruptedException ex) 
            {
                Thread.currentThread().interrupt();
            }
        }
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        // TODO: some final clean up could be here also
        System.out.println("JMX stopped");
    }
}
}

การควบคุมแอปพลิเคชัน:
เรียกใช้ด้วยการหยุดหรือเริ่มเป็นอาร์กิวเมนต์บรรทัดคำสั่ง

public class ThreadMonitorConsole
{

public static void main(String[] args)
{
    try
    {   
        // connecting to JMX
        System.out.println("Connect to JMX service.");
        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
        JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
        MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();

        // Construct proxy for the the MBean object
        ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor");
        ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true);

        System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running");

        // parse command line arguments
        if(args[0].equalsIgnoreCase("start"))
        {
            System.out.println("Invoke \"start\" method");
            mbeanProxy.start();
        }
        else if(args[0].equalsIgnoreCase("stop"))
        {
            System.out.println("Invoke \"stop\" method");
            mbeanProxy.stop();
        }

        // clean up and exit
        jmxc.close();
        System.out.println("Done.");    
    }
    catch(Exception e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
}


แค่นั้นแหละ. :-)


7

อีกวิธีหนึ่ง: แอปพลิเคชันของคุณสามารถเปิดฐานข้อมูลเซิร์ฟเวอร์และรอให้ข้อมูลมาถึง ตัวอย่างเช่นสตริงที่มีคำว่า "magic" :) จากนั้นตอบสนองเพื่อทำการปิดระบบ: System.exit () คุณสามารถส่งข้อมูลดังกล่าวไปยัง socke โดยใช้แอปพลิเคชันภายนอกเช่น telnet


3

นี่เป็นวิธีแก้ปัญหาที่ยุ่งยาก แต่พกพาได้:

  • ในแอปพลิเคชันของคุณใช้ hook การปิดระบบ
  • เมื่อคุณต้องการที่จะปิดตัวลง JVM ของคุณได้อย่างสง่างามติดตั้งตัวแทน Javaที่เรียกSystem.exit ()ใช้แนบ API

ฉันติดตั้ง Java Agent มีอยู่ใน Github: https://github.com/everit-org/javaagent-shutdown

คำอธิบายโดยละเอียดเกี่ยวกับโซลูชันมีอยู่ที่นี่: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/


2

คำถามที่คล้ายกันที่นี่

Finalizers ใน Java ไม่ดี พวกเขาเพิ่มค่าใช้จ่ายจำนวนมากในการเก็บขยะ หลีกเลี่ยงเมื่อทำได้

shutdownHook จะถูกเรียกเมื่อ VM กำลังปิดเครื่องเท่านั้น ฉันคิดว่าดีมากอาจทำในสิ่งที่คุณต้องการ


1
ดังนั้น shutdownHook ถูกเรียกในทุกกรณี? จะเกิดอะไรขึ้นถ้า 'ฆ่า' กระบวนการจากเชลล์?
Ma99uS

ไม่ใช่ว่าคุณจะฆ่ามัน -9 :)
Steve g

0

การส่งสัญญาณใน Linux ทำได้ด้วย "kill" (man kill สำหรับสัญญาณที่มี) คุณต้องใช้รหัสกระบวนการในการทำเช่นนั้น (ps ax | grep java) หรืออะไรทำนองนั้นหรือเก็บ id กระบวนการเมื่อสร้างกระบวนการ (ใช้ในไฟล์เริ่มต้น linux ส่วนใหญ่ดู /etc/init.d)

การส่งสัญญาณแบบพกพาสามารถทำได้โดยการรวม SocketServer ในแอปพลิเคชัน java ของคุณ ไม่ใช่เรื่องยากและให้อิสระในการส่งคำสั่งตามที่คุณต้องการ

หากคุณหมายถึงประโยคสุดท้ายแทนคำสั่งสุดท้าย พวกเขาไม่ได้รับการขยายเมื่อเรียก System.exit () Finalizers ควรใช้งานได้ แต่ไม่ควรทำอะไรที่สำคัญไปกว่านั้น แต่พิมพ์คำสั่ง debug พวกมันอันตราย


0

ขอบคุณสำหรับคำตอบ ปิดตะขอตะเข็บเหมือนบางอย่างที่ใช้ได้ในกรณีของฉัน แต่ฉันก็พบกับสิ่งที่เรียกว่าการตรวจสอบและการจัดการถั่ว:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
ที่ให้ความเป็นไปได้ที่ดีสำหรับการตรวจสอบระยะไกลและการจัดการ ของกระบวนการ java (ได้รับการแนะนำใน Java 5)

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