เมื่อใดที่เราควรเรียก System.exit ใน Java


193

ใน Java ความแตกต่างที่มีหรือไม่มีSystem.exit(0)ในรหัสต่อไปนี้คืออะไร

public class TestExit
{      
    public static void main(String[] args)
    { 
        System.out.println("hello world");

        System.exit(0);  // is it necessary? And when it must be called? 
    }      
}

เอกสารกล่าวว่า "วิธีการนี้ไม่เคยส่งกลับตามปกติ." มันหมายความว่าอะไร?

คำตอบ:


207

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

"วิธีนี้จะไม่ส่งคืนตามปกติ" หมายความว่าวิธีจะไม่กลับมา; เมื่อเธรดไปที่นั่นมันจะไม่กลับมา

อีกวิธีหนึ่งที่อาจพบได้บ่อยกว่าคือการออกจากโปรแกรมคือการไปให้ถึงจุดสิ้นสุดของmainวิธีการ แต่ถ้ามีเธรดที่ไม่ใช่ daemon กำลังทำงานอยู่เธรดเหล่านั้นจะไม่ถูกปิดดังนั้น JVM จะไม่ออก ดังนั้นหากคุณมีเธรดที่ไม่ใช่ daemon ใด ๆ คุณต้องมีวิธีอื่น (นอกเหนือจาก hooks การปิดระบบ) เพื่อปิดเธรดที่ไม่ใช่ daemon ทั้งหมดและปล่อยทรัพยากรอื่น ๆ หากไม่มีเธรดอื่นที่ไม่ใช่ daemon การส่งคืนจากmainจะปิด JVM และจะเรียกใช้ hooks การปิด

ด้วยเหตุผลบางประการการปิดระบบของ hooks ดูเหมือนจะเป็นกลไกที่ไม่คุ้มค่าและเข้าใจผิดและผู้คนต่างคิดค้นล้อด้วยแฮ็กแบบกำหนดเองที่เป็นกรรมสิทธิ์ทุกประเภทเพื่อออกจากโปรแกรมของพวกเขา ฉันอยากจะแนะนำให้ใช้ hooks ปิด; มันมีอยู่ในรันไทม์มาตรฐานที่คุณจะใช้อยู่ดี


10
"วิธีนี้จะไม่ส่งคืนตามปกติ" หมายความว่าวิธีจะไม่กลับมา; เมื่อเธรดไปที่นั่นมันจะไม่กลับมา โดยเฉพาะอย่างยิ่งโปรดทราบว่านี่หมายความว่าคุณไม่สามารถทดสอบหน่วยที่ใช้เรียก System.exit (0) ...
Bill Michell

5
-1 ไม่ถูกต้อง hooks shutdown hooks จะทำงานหาก JVM ยุติลงตามปกติไม่ว่าจะเกิดจาก System.exit หรือการสิ้นสุดของ main () ดูzx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang / …
พฤศจิกายน

31
@sleske: การยกเลิก main () ไม่เพียงพอหากมีเธรดที่ไม่ใช่ daemon อยู่รอบตัว การปิดระบบจะเริ่มต้นได้หลังจากเธรดที่ไม่ใช่ daemon ล่าสุดถูกยกเลิกเว้นแต่คุณจะเรียก System.exit () อย่างชัดเจน ที่ระบุไว้อย่างชัดเจนในเอกสารประกอบ Runtime
Joonas Pulakka

3
โปรดทราบว่าหาก hook ปิดระบบของคุณนั้นขึ้นอยู่กับเธรดที่เรียกว่า System.exit คุณจะหยุดชะงัก
djechlin

8
สิ่งที่ต้องเพิ่มถ้ามีคนหยุดใช้งาน runtime ตะขอการปิดจะไม่ทำงาน ( Runtime.getRuntime().halt())
Rogue

50

ในกรณีนั้นไม่จำเป็น จะไม่มีการเริ่มหัวข้อเพิ่มเติมคุณไม่ได้เปลี่ยนรหัสการออก (ซึ่งเป็นค่าเริ่มต้นที่ 0) โดยพื้นฐานแล้วมันไม่มีจุดหมาย

เมื่อเอกสารบอกว่าวิธีการไม่ส่งคืนตามปกติหมายความว่าบรรทัดต่อมาของโค้ดนั้นไม่สามารถเข้าถึงได้อย่างมีประสิทธิภาพแม้ว่าคอมไพเลอร์ไม่ทราบว่า:

System.exit(0);
System.out.println("This line will never be reached");

อาจมีข้อยกเว้นเกิดขึ้นหรือ VM จะยกเลิกก่อนส่งคืน มันจะไม่ "กลับมา"

เป็นเรื่องยากมากที่จะโทรมาเยี่ยมSystem.exit()IME มันสมเหตุสมผลถ้าคุณเขียนเครื่องมือบรรทัดคำสั่งและคุณต้องการระบุข้อผิดพลาดผ่านรหัสออกแทนที่จะทิ้งข้อยกเว้น ... แต่ฉันจำไม่ได้ว่าครั้งสุดท้ายที่ฉันใช้มันในรหัสการผลิตปกติ .


2
ทำไมคอมไพเลอร์ไม่ทราบเช่นนั้น? System.exit () ไม่เพียงพอที่จะรับประกันรหัสตรวจจับที่เฉพาะเจาะจงใช่หรือไม่
Bart van Heukelom

3
@Bart: ไม่ฉันไม่คิดอย่างนั้น การใส่กรณีพิเศษในภาษาสำหรับสิ่งต่าง ๆ เช่นนี้จะเพิ่มความซับซ้อนทางภาษาและมีประโยชน์น้อยมาก
Jon Skeet

ฉันคิดว่า "ไม่กลับมาปกติ" เกี่ยวข้องกับคำว่า (JLS ส่วนที่ 14.1) ฉันผิดหรือเปล่า?
aioobe

@aioobe: ไม่คุณพูดถูก System.exit()จะไม่ดำเนินการตามปกติ - มันจะสมบูรณ์พร้อมด้วยข้อยกเว้นหรือด้วยการปิด VM (ซึ่งไม่ได้ครอบคลุมใน JLS)
Jon Skeet

1
@piechuckerr: อะไรทำให้คุณคิดอย่างนั้น? มันสมเหตุสมผลอย่างยิ่งที่จะเรียกมันด้วยค่าอื่นที่ไม่ใช่ 0 เพื่อระบุเชลล์ที่เรียก (หรืออะไรก็ตาม) ที่โปรแกรมพบข้อผิดพลาด
Jon Skeet

15

System.exit(0)ยุติ JVM ในตัวอย่างง่ายๆเช่นนี้มันเป็นการยากที่จะเข้าใจความแตกต่าง พารามิเตอร์จะถูกส่งกลับไปยัง OS และโดยปกติจะใช้เพื่อบ่งบอกถึงการเลิกจ้างที่ผิดปกติ (เช่นข้อผิดพลาดร้ายแรงบางอย่าง) ดังนั้นหากคุณเรียก java จากแบตช์ไฟล์หรือสคริปต์เชลล์คุณจะสามารถรับค่านี้และรับแนวคิด หากแอปพลิเคชันสำเร็จ

มันจะสร้างผลกระทบค่อนข้างมากถ้าคุณโทรSystem.exit(0)หาแอปพลิเคชันที่ปรับใช้กับแอปพลิเคชันเซิร์ฟเวอร์ (คิดก่อนที่จะลอง)


15

วิธีไม่เคยส่งคืนเนื่องจากเป็นจุดสิ้นสุดของโลกและไม่มีรหัสของคุณจะถูกดำเนินการต่อไป

แอปพลิเคชันของคุณในตัวอย่างของคุณจะออกจากที่จุดเดียวกันในรหัส แต่ถ้าคุณใช้ System.exit คุณมีตัวเลือกในการส่งคืนรหัสที่กำหนดเองไปยังสภาพแวดล้อมเช่นพูด

System.exit(42);

ใครจะใช้ประโยชน์จากรหัสทางออกของคุณ? สคริปต์ที่เรียกว่าแอปพลิเคชัน ทำงานได้ใน Windows, Unix และสภาพแวดล้อมแบบสคริปต์อื่น ๆ ทั้งหมด

ทำไมต้องส่งคืนรหัส หากต้องการพูดสิ่งต่างๆเช่น "ฉันไม่สำเร็จ", "ฐานข้อมูลไม่ตอบรับ"

ในการดูวิธีรับค่ารหัสออกและใช้ในยูนิกซ์เชลล์สคริปต์หรือสคริปต์ Windows cmd คุณอาจตรวจสอบคำตอบนี้ในเว็บไซต์นี้


12

ในแอปพลิเคชันที่อาจมี hooks การปิดที่ซับซ้อนวิธีนี้ไม่ควรเรียกจากเธรดที่ไม่รู้จัก System.exitไม่ออกจากปกติเนื่องจากการโทรจะบล็อกจนกว่า JVM จะถูกยกเลิก ราวกับว่าโค้ดใด ๆ ที่กำลังทำงานอยู่ซึ่งมีปลั๊กไฟดึงอยู่ก่อนที่มันจะเสร็จสิ้น การเรียกSystem.exitจะเริ่มการขอปิดระบบของโปรแกรมและเธรดใดก็ตามที่การโทรSystem.exitจะถูกบล็อกจนกว่าโปรแกรมจะสิ้นสุด สิ่งนี้มีความหมายว่าถ้า hook การปิดระบบส่งงานไปยังเธรดที่System.exitถูกเรียกใช้โปรแกรมจะหยุดชะงัก

ฉันจัดการสิ่งนี้ในรหัสของฉันด้วยสิ่งต่อไปนี้:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}

ฉันยังมีปัญหาที่ System.exit จะไม่จบ JVM จริง มันเกิดขึ้นภายใต้เงื่อนไขบางอย่างเท่านั้น เธรดที่ฉันทำไม่ได้ให้ข้อมูลเชิงลึกใด ๆ เกี่ยวกับตัวจัดการเธรด / ปิดที่กำลังบล็อกการปิด การใช้รหัสที่อธิบายในความคิดเห็นช่วยให้ฉันแก้ปัญหาได้!
ไก่

7

แม้ว่าคำตอบนั้นมีประโยชน์จริง ๆ แต่ก็มีบางรายละเอียดเพิ่มเติม ฉันหวังว่าด้านล่างจะช่วยให้เข้าใจกระบวนการปิดใน java นอกเหนือจากคำตอบข้างต้น:

  1. ในการปิดอย่างเป็นระเบียบ * JVM จะเริ่ม hooks การปิดที่ลงทะเบียนไว้ทั้งหมดก่อน hooks การปิดระบบเป็นเธรดที่ไม่ได้สตาร์ทที่ลงทะเบียนกับ Runtime.addShutdownHook
  2. JVM ไม่รับประกันในการสั่งซื้อที่ตะขอสำหรับปิดเครื่องเริ่มต้นขึ้น หากแอปพลิเคชันเธรด (daemon หรือ nondaemon) ใด ๆ ยังคงทำงานในเวลาปิดระบบจะยังคงทำงานพร้อมกับกระบวนการปิดพวกเขายังคงทำงานควบคู่กันไปกับการปิดระบบ
  3. เมื่อ hooks การปิดทั้งหมดเสร็จสิ้นแล้ว JVM อาจเลือกที่จะรัน finalizers หาก runFinalizersOnExit เป็นจริงจากนั้นหยุดการทำงาน
  4. JVM ไม่พยายามหยุดหรือขัดจังหวะแอปพลิเคชันเธรดใด ๆ ที่ยังคงทำงานอยู่ในเวลาปิดเครื่อง จะถูกยกเลิกทันทีเมื่อ JVM หยุดทำงานในที่สุด
  5. หากการปิดระบบ hooks หรือ finalizers ไม่เสร็จสมบูรณ์กระบวนการปิดระบบอย่างเป็นระบบจะ“ หยุด” และ JVM จะต้องปิดตัวลงอย่างกะทันหัน
  6. ในการปิดระบบอย่างกะทันหัน JVM ไม่จำเป็นต้องทำสิ่งใดนอกจากหยุด JVM hooks ปิดจะไม่ทำงาน

PS: JVM ที่สามารถปิดในทั้งระเบียบหรือทันทีทันใดลักษณะ

  1. การปิดระบบอย่างเป็นระเบียบนั้นเริ่มต้นเมื่อเธรด“ ปกติ” (nondaemon) สุดท้ายถูกยกเลิกใครบางคนเรียก System.exit หรือโดยวิธีการเฉพาะแพลตฟอร์มอื่น ๆ (เช่นการส่ง SIGINT หรือกดปุ่ม Ctrl-C)
  2. ในขณะที่ข้างต้นเป็นวิธีมาตรฐานและเป็นที่นิยมสำหรับ JVM ที่จะปิดมันก็สามารถปิดได้ทันทีโดยการเรียก Runtime.halt หรือโดยการฆ่ากระบวนการ JVM ผ่านระบบปฏิบัติการ (เช่นการส่ง SIGKILL)

7

เราไม่ควรเรียกSystem.exit(0)เหตุผลเหล่านี้:

  1. มันเป็น "โกโตะ" และ "โกโตส" ที่ซ่อนอยู่ทำให้การไหลของการควบคุมพังไป การพึ่งพาตะขอในบริบทนี้เป็นการทำแผนที่จิตที่นักพัฒนาทุกคนในทีมต้องระวัง
  2. การออกจากโปรแกรม "ปกติ" จะมอบรหัสการออกให้กับระบบปฏิบัติการเช่นเดียวกับ System.exit(0)เพื่อให้ซ้ำซ้อน

    หากโปรแกรมของคุณไม่สามารถออกจาก "ปกติ" คุณสูญเสียการควบคุมการพัฒนา [การออกแบบ] คุณควรควบคุมสถานะของระบบได้อย่างเต็มที่

  3. ปัญหาการเขียนโปรแกรมเช่นการรันเธรดที่ไม่ได้หยุดตามปกติจะถูกซ่อน
  4. คุณอาจพบสถานะแอปพลิเคชันที่ไม่สอดคล้องกันของเธรดขัดข้อง (อ้างถึง # 3)

โดยวิธีการ: การส่งคืนโค้ดส่งคืนอื่นที่ไม่ใช่ 0 นั้นสมเหตุสมผลถ้าคุณต้องการระบุการยุติโปรแกรมที่ผิดปกติ


2
"คุณควรควบคุมสถานะของระบบได้อย่างเต็มที่" ที่เป็นไปไม่ได้ใน Java เฟรมเวิร์ก IoC และแอ็พพลิเคชันเซิร์ฟเวอร์เป็นเพียง 2 วิธีที่ได้รับความนิยมและใช้กันอย่างแพร่หลายในการยกเลิกการควบคุมสถานะของระบบ และมันก็ใช้ได้ดี
mhlz

โปรดดำเนินการ System.exit (0) ในเซิร์ฟเวอร์แอปพลิเคชันของคุณและบอกฉันเกี่ยวกับปฏิกิริยาของผู้ดูแลเซิร์ฟเวอร์ของคุณ ... คุณพลาดหัวข้อคำถามนี้และคำตอบของฉัน
oopexpert

บางทีฉันอาจจะไม่ชัดเจนพอ คุณควรควบคุมสถานะของระบบได้อย่างเต็มที่ตลอดเวลา ใช่มีการย้ายความรับผิดชอบบางส่วนไปยังแอพพลิเคชันเซิร์ฟเวอร์และ IoC แต่ฉันไม่ได้พูดถึงเรื่องนั้น
oopexpert

5

จำเป็นต้องมีระบบ

  • เมื่อคุณต้องการส่งคืนรหัสข้อผิดพลาดที่ไม่ใช่ 0
  • เมื่อคุณต้องการออกจากโปรแกรมของคุณจากที่ที่ไม่ใช่หลัก ()

ในกรณีของคุณมันทำสิ่งเดียวกับกลับจากหลัก


คุณหมายถึงอะไรกับหลัง? วิธีที่เหมาะสมในการปิดโปรแกรมคือหยุดเธรดทั้งหมด (อย่างดี) การย้ายที่ไม่จำเป็นต้องเริ่มจากเธรดหลักดังนั้นจึงไม่เหมือนกับexitตัวเลือกเดียวของคุณที่นั่น
Bart van Heukelom

@Bart: สิ่งที่คุณอธิบายเป็นหนึ่งในวิธีที่เป็นไปได้ที่จะปิดโปรแกรมที่ไม่ได้วิธีการที่เหมาะสม ไม่มีอะไรผิดปกติในการใช้ hooks ปิดเพื่อหยุดเธรดทั้งหมดอย่างดี exitและตะขอปิดจะเปิดตัวด้วย ไม่ใช่ตัวเลือกเดียว แต่เป็นตัวเลือกที่คุ้มค่าที่จะพิจารณา
Joonas Pulakka

แต่สิ่งที่ฉันรวบรวมจากเอกสารตะขอการปิดมีความละเอียดอ่อนมากและอาจไม่มีเวลามากที่จะทำสิ่งที่คุณต้องการให้พวกเขาทำ โดยทั้งหมดใช้พวกเขาเพื่อออกอย่างในกรณีที่ปิดระบบปฏิบัติการหรืออะไร แต่ถ้าคุณจะเรียก System.exit () ด้วยตัวคุณเองฉันคิดว่ามันจะดีกว่าที่จะทำรหัสปิดก่อน (หลังจากที่คุณอาจจะไม่ ต้องการ System.exit () อีกต่อไป)
Bart van Heukelom

1
ตะขอการปิดเครื่องมีเวลาทั้งหมดที่ใช้ในการเสร็จสิ้น VM จะไม่หยุดก่อนที่ hooks ทั้งหมดจะถูกส่งคืน เป็นไปได้จริง ๆ ที่จะป้องกันไม่ให้ VM หยุดทำงานโดยการลงทะเบียน hook การปิดซึ่งใช้เวลานานในการประมวลผล แต่แน่นอนว่ามันเป็นไปได้ที่จะทำลำดับการปิดในรูปแบบต่าง ๆ ทางเลือก
Joonas Pulakka

การส่งคืนจาก main จะไม่ปิดหากมีเธรดที่ไม่ใช่ daemon อื่น ๆ
djechlin

4

Java Language Specificationบอกว่า

ออกจากโปรแกรม

โปรแกรมจะหยุดกิจกรรมทั้งหมดและออกเมื่อมีสิ่งใดสิ่งหนึ่งเกิดขึ้น:

เธรดทั้งหมดที่ไม่ใช่ daemon เธรดสิ้นสุดลง

บางเธรดเรียกใช้เมธอด exit ของคลาสรันไทม์หรือคลาสระบบและการดำเนินการออกจะไม่ถูกห้ามโดยตัวจัดการความปลอดภัย

หมายความว่าคุณควรใช้มันเมื่อคุณมีโปรแกรมใหญ่ (ดีที่สุดเพื่อไม่ให้ใหญ่กว่าโปรแกรมนี้) และต้องการที่จะดำเนินการให้เสร็จสิ้น


3

หากคุณมีโปรแกรมอื่นที่ทำงานอยู่ใน JVM และคุณใช้ System.exit โปรแกรมที่สองนั้นก็จะถูกปิดเช่นกัน ลองนึกภาพตัวอย่างที่คุณรันงานจาวาบนโหนดคลัสเตอร์และโปรแกรมจาวาที่จัดการโหนดคลัสเตอร์ทำงานใน JVM เดียวกัน หากงานจะใช้ System.exit มันจะไม่เพียง แต่ออกจากงาน แต่ยัง "ปิดโหนดที่สมบูรณ์" คุณจะไม่สามารถส่งงานอื่นไปยังโหนดคลัสเตอร์นั้นได้เนื่องจากโปรแกรมการจัดการถูกปิดโดยไม่ตั้งใจ

ดังนั้นห้ามใช้ System.exit หากคุณต้องการควบคุมโปรแกรมของคุณจากโปรแกรมจาวาอื่นภายใน JVM เดียวกัน

ใช้ System.exit หากคุณต้องการปิด JVM แบบสมบูรณ์ตามวัตถุประสงค์และหากคุณต้องการใช้ประโยชน์จากความเป็นไปได้ที่ได้อธิบายไว้ในคำตอบอื่น ๆ (เช่น shut down hooks: Java shutdown hook , ค่าส่งกลับไม่ใช่ศูนย์สำหรับบรรทัดคำสั่ง โทรศัพท์: วิธีรับสถานะการออกของโปรแกรม Java ในไฟล์แบตช์ Windows )

ดูที่ข้อยกเว้นรันไทม์: System.exit (จำนวน) หรือโยน RuntimeException จาก main หรือไม่

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