Khi nào chúng ta nên gọi System.exit trong Java


193

Trong Java, sự khác biệt có hoặc không có System.exit(0)trong đoạn mã sau là gì?

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? 
    }      
}

Các tài liệu cho biết: "Phương pháp này không bao giờ trở lại bình thường." Nó có nghĩa là gì?

Câu trả lời:


207

System.exit()có thể được sử dụng để chạy các móc tắt máy trước khi thoát khỏi chương trình. Đây là một cách thuận tiện để xử lý tắt máy trong các chương trình lớn hơn, trong đó tất cả các phần của chương trình không thể (và không nên) biết về nhau. Sau đó, nếu ai đó muốn thoát, anh ta có thể gọi đơn giản System.exit()và các móc tắt máy (nếu được thiết lập đúng cách) sẽ thực hiện tất cả các nghi thức tắt máy cần thiết như đóng tệp, giải phóng tài nguyên, v.v.

"Phương pháp này không bao giờ trở lại bình thường." có nghĩa là phương thức sẽ không trở lại; một khi một chủ đề đến đó, nó sẽ không trở lại.

Một cách khác, có thể phổ biến hơn, để thoát khỏi một chương trình là chỉ đơn giản là đi đến cuối mainphương thức. Nhưng nếu có bất kỳ luồng không phải daemon nào đang chạy, chúng sẽ không bị tắt và do đó JVM sẽ không thoát. Do đó, nếu bạn có bất kỳ luồng không phải daemon nào như vậy, bạn cần một số phương tiện khác (ngoài móc tắt máy) để tắt tất cả các luồng không phải daemon và giải phóng các tài nguyên khác. Nếu không có các luồng không phải daemon nào khác, quay trở lại mainsẽ tắt JVM và sẽ gọi các móc tắt máy.

Vì một số lý do, móc tắt máy dường như là một cơ chế bị đánh giá thấp và bị hiểu lầm, và mọi người đang phát minh lại bánh xe với tất cả các loại hack tùy chỉnh độc quyền để thoát khỏi chương trình của họ. Tôi sẽ khuyến khích sử dụng móc tắt máy; tất cả đều có trong Runtime tiêu chuẩn mà bạn sẽ sử dụng.


10
"Phương pháp này không bao giờ trở lại bình thường." có nghĩa là phương thức sẽ không trở lại; một khi một chủ đề đến đó, nó sẽ không trở lại. Đặc biệt lưu ý rằng điều này có nghĩa là bạn không thể đơn vị kiểm tra một phương thức thực hiện cuộc gọi System.exit (0) ...
Bill Michell

5
-1 Không chính xác. Các hook shutdown sẽ chạy nếu JVM chấm dứt bình thường, bất kể đó là vì System.exit hay chấm dứt main (). Xem zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang / Lỗi
sleske

31
@sleske: Chấm dứt main () là không đủ nếu có các luồng không phải daemon khác xung quanh. Tắt máy chỉ được bắt đầu sau khi luồng không phải daemon cuối cùng kết thúc, trừ khi bạn gọi System.exit () một cách rõ ràng. Điều đó được nêu rõ trong tài liệu Thời gian chạy.
Joonas Pulakka

3
Lưu ý rằng nếu móc tắt máy của bạn lần lượt dựa vào luồng có tên System.exit, bạn sẽ bế tắc.
djechlin

8
Một cái gì đó để thêm vào, nếu ai đó tạm dừng thời gian chạy, móc tắt máy sẽ không chạy ( Runtime.getRuntime().halt())
Rogue

50

Trong trường hợp đó, nó không cần thiết. Không có chủ đề bổ sung nào sẽ được khởi động, bạn không thay đổi mã thoát (mặc định là 0) - về cơ bản là vô nghĩa.

Khi các tài liệu nói rằng phương thức không bao giờ trả về bình thường, điều đó có nghĩa là dòng mã tiếp theo thực sự không thể truy cập được, mặc dù trình biên dịch không biết rằng:

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

Một ngoại lệ sẽ được đưa ra, hoặc VM sẽ chấm dứt trước khi quay trở lại. Nó sẽ không bao giờ "chỉ trở lại".

Rất hiếm khi gọi System.exit()IME. Sẽ có nghĩa nếu bạn đang viết một công cụ dòng lệnh và bạn muốn chỉ ra lỗi thông qua mã thoát thay vì chỉ ném một ngoại lệ ... nhưng tôi không thể nhớ lần cuối cùng tôi sử dụng nó trong mã sản xuất bình thường .


2
Tại sao trình biên dịch không biết điều đó? Không System.exit () đủ đặc biệt để đảm bảo mã phát hiện cụ thể?
Bart van Heukelom

3
@Bart: Không, tôi không nghĩ vậy. Đặt các trường hợp đặc biệt vào ngôn ngữ cho những thứ như thế này làm tăng thêm sự phức tạp của ngôn ngữ với rất ít lợi ích.
Jon Skeet

Tôi nghĩ rằng "không bao giờ trở lại bình thường" phải làm với "hoàn thành đột ngột" của tuyên bố. (JLS phần 14.1). Liệu tôi có sai?
aioobe

@aioobe: Không, bạn nói đúng. System.exit()sẽ không bao giờ hoàn thành bình thường - nó sẽ luôn luôn hoàn thành với một ngoại lệ hoặc tắt VM (điều này không thực sự được đề cập trong JLS).
Jon Skeet

1
@piechuckerr: Điều gì khiến bạn nghĩ như vậy? Hoàn toàn hợp lý khi gọi nó với giá trị khác 0, để chỉ ra cho vỏ gọi (hoặc bất cứ điều gì) rằng chương trình đã gặp lỗi.
Jon Skeet

15

Phương thức không bao giờ trả về vì đó là ngày tận thế và không có mã nào của bạn sẽ được thực thi tiếp theo.

Ứng dụng của bạn, trong ví dụ của bạn, sẽ thoát khỏi cùng một vị trí trong mã, nhưng, nếu bạn sử dụng System.exit. bạn có tùy chọn trả lại mã tùy chỉnh cho môi trường, như, nói

System.exit(42);

Ai sẽ sử dụng mã thoát của bạn? Một kịch bản gọi là ứng dụng. Hoạt động trong Windows, Unix và tất cả các môi trường script khác.

Tại sao trả lại một mã? Để nói những điều như "Tôi đã không thành công", "Cơ sở dữ liệu không trả lời".

Để xem cách lấy giá trị od mã thoát và sử dụng nó trong tập lệnh shell unix hoặc tập lệnh cmd windows, bạn có thể kiểm tra câu trả lời này trên trang web này


14

System.exit(0)chấm dứt JVM. Trong các ví dụ đơn giản như thế này, thật khó để nhận ra sự khác biệt. Tham số được truyền lại cho HĐH và thường được sử dụng để biểu thị sự chấm dứt bất thường (ví dụ: một số lỗi nghiêm trọng), vì vậy nếu bạn gọi java từ tệp bó hoặc tập lệnh shell, bạn có thể nhận được giá trị này và có ý tưởng nếu ứng dụng thành công

Nó sẽ tạo ra một tác động khá lớn nếu bạn gọi System.exit(0)một ứng dụng được triển khai đến một máy chủ ứng dụng (suy nghĩ về nó trước khi bạn thử nó).


11

Trong các ứng dụng có thể có các móc tắt máy phức tạp, không nên gọi phương thức này từ một luồng không xác định. System.exitkhông bao giờ thoát bình thường vì cuộc gọi sẽ chặn cho đến khi JVM kết thúc. Như thể bất cứ mã nào đang chạy có phích cắm điện được kéo vào trước khi nó có thể kết thúc. Việc gọi System.exitsẽ bắt đầu các móc tắt máy của chương trình và bất kỳ luồng nào mà các cuộc gọi System.exitsẽ chặn cho đến khi kết thúc chương trình. Điều này có hàm ý rằng nếu móc tắt máy lần lượt gửi một tác vụ đến luồng mà từ đó System.exitđược gọi, chương trình sẽ bế tắc.

Tôi đang xử lý mã này trong mã của mình bằng cách sau:

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

Tôi cũng gặp vấn đề khi System.exit không thực sự kết thúc JVM. Nó chỉ xảy ra trong một số điều kiện nhất định. Một luồng mà tôi đã không cung cấp cho tôi bất kỳ hiểu biết nào về việc xử lý luồng / xử lý tắt máy nào đang chặn tắt máy. Sử dụng mã được mô tả trong nhận xét đã giúp tôi giải quyết nó!
Kai

7

Mặc dù câu trả lời thực sự hữu ích nhưng một số cách nó bỏ lỡ một số chi tiết bổ sung. Tôi hy vọng dưới đây sẽ giúp hiểu quá trình tắt máy trong java, ngoài câu trả lời ở trên:

  1. Trong một tắt máy có trật tự *, trước tiên JVM bắt đầu tất cả các móc tắt máy đã đăng ký. Móc tắt máy là các luồng chưa được khởi động được đăng ký với Runtime.addShutdownHook.
  2. JVM không đảm bảo về thứ tự bắt đầu móc tắt máy. Nếu bất kỳ luồng ứng dụng nào (daemon hoặc nondaemon) vẫn đang chạy khi tắt máy, chúng sẽ tiếp tục chạy đồng thời với quá trình tắt máy .
  3. Khi tất cả các móc tắt máy đã hoàn thành, JVM có thể chọn chạy bộ hoàn thiện nếu runFinalulatorsOnExit là đúng và sau đó tạm dừng.
  4. JVM không cố gắng dừng hoặc ngắt bất kỳ luồng ứng dụng nào vẫn đang chạy khi tắt máy; chúng bị chấm dứt đột ngột khi JVM cuối cùng dừng lại.
  5. Nếu các móc tắt máy hoặc bộ hoàn thiện không hoàn thành, thì quá trình tắt máy có trật tự, treo máy và JVM phải bị tắt đột ngột.
  6. Khi tắt máy đột ngột, JVM không bắt buộc phải làm bất cứ điều gì ngoài việc tạm dừng JVM; móc tắt máy sẽ không chạy.

PS: JVM có thể tắt theo cách có trật tự hoặc đột ngột .

  1. Việc tắt máy có trật tự được bắt đầu khi luồng cuối cùng bình thường (nondaemon), một người nào đó gọi System.exit hoặc bằng các phương tiện cụ thể khác của nền tảng (như gửi SIGINT hoặc nhấn Ctrl-C).
  2. Mặc dù ở trên là cách tiêu chuẩn và ưa thích để JVM tắt, nhưng nó cũng có thể bị tắt đột ngột bằng cách gọi Runtime.halt hoặc bằng cách giết tiến trình JVM thông qua hệ điều hành (chẳng hạn như gửi SIGKILL).

7

KHÔNG BAO GIỜ nên gọi System.exit(0)vì những lý do sau:

  1. Đó là một "goto" và "gotos" ẩn phá vỡ dòng điều khiển. Dựa vào các móc trong bối cảnh này là một bản đồ tinh thần mà mọi nhà phát triển trong nhóm phải nhận thức được.
  2. Thoát khỏi chương trình "thông thường" cung cấp cùng một mã thoát cho hệ điều hành System.exit(0)vì vậy nó là dự phòng.

    Nếu chương trình của bạn không thể thoát "bình thường", bạn đã mất quyền kiểm soát sự phát triển của mình [thiết kế]. Bạn phải luôn kiểm soát hoàn toàn trạng thái hệ thống.

  3. Các vấn đề lập trình như chạy các luồng không dừng thường trở nên ẩn.
  4. Bạn có thể gặp một trạng thái ứng dụng không nhất quán làm gián đoạn các luồng bất thường. (Tham khảo số 3)

Nhân tiện: Trả lại các mã trả về khác 0 có nghĩa nếu bạn muốn chỉ ra việc chấm dứt chương trình bất thường.


2
"Bạn nên luôn luôn kiểm soát hoàn toàn trạng thái hệ thống." Điều đó chỉ đơn giản là không thể có trong Java. Các khung công tác và máy chủ ứng dụng IoC chỉ là 2 cách rất phổ biến và được sử dụng rộng rãi mà bạn từ bỏ quyền kiểm soát trạng thái hệ thống. Và điều đó hoàn toàn tốt.
mhlz

Vui lòng thực thi System.exit (0) trong máy chủ ứng dụng của bạn và cho tôi biết về phản ứng của quản trị viên máy chủ của bạn ... Bạn đã bỏ lỡ chủ đề của câu hỏi này và câu trả lời của tôi.
oopexpert

Có lẽ tôi đã không đủ rõ ràng. Bạn phải luôn có toàn quyền kiểm soát trạng thái hệ thống mà bạn chịu trách nhiệm. Có, một số trách nhiệm đã được chuyển sang các máy chủ ứng dụng và IoC. Nhưng tôi đã không nói về điều đó.
oopexpert

5

System.exit là cần thiết

  • khi bạn muốn trả về mã lỗi khác 0
  • khi bạn muốn thoát khỏi chương trình của mình từ một nơi không phải là chính ()

Trong trường hợp của bạn, nó thực hiện chính xác điều tương tự như trả về từ chính.


Bạn có ý nghĩa gì với cái sau? Cách thích hợp để tắt chương trình là dừng tất cả các luồng (độc đáo), một động thái không phải bắt đầu từ luồng chính, vì vậy nó không giống như exitlà lựa chọn duy nhất của bạn ở đó.
Bart van Heukelom

@Bart: những gì bạn mô tả là một trong những cách có thể để tắt chương trình, không phải là cách thích hợp . Không có gì sai trong việc sử dụng móc tắt máy để dừng tất cả các luồng độc đáo. Và móc tắt máy được đưa ra với exit. Không phải là lựa chọn duy nhất, nhưng chắc chắn là một lựa chọn đáng để xem xét.
Joonas Pulakka

Nhưng những gì tôi thu thập được từ các tài liệu, móc tắt máy rất tinh tế và có thể không có nhiều thời gian để làm những gì bạn muốn họ làm. Bằng mọi cách, hãy sử dụng chúng để thoát ra một cách độc đáo trong trường hợp tắt hệ điều hành hoặc một cái gì đó, nhưng nếu bạn tự gọi System.exit (), tôi nghĩ tốt hơn là nên thực hiện mã tắt máy trước đó (sau đó bạn có thể sẽ không cần System.exit () nữa)
Bart van Heukelom

1
Móc tắt máy có tất cả thời gian họ cần để hoàn thành. VM không bị dừng trước khi tất cả các hook trở lại. Thực sự có thể ngăn VM thoát khỏi bằng cách đăng ký một móc tắt máy mất rất nhiều thời gian để thực thi. Nhưng tất nhiên, có thể thực hiện chuỗi tắt máy theo nhiều cách khác nhau.
Joonas Pulakka

Trở về từ chính sẽ không tắt nếu có các luồng không phải daemon khác.
djechlin

4

Đặc tả ngôn ngữ Java nói rằng

Thoát chương trình

Một chương trình chấm dứt tất cả hoạt động của nó và thoát khi một trong hai điều xảy ra:

Tất cả các chủ đề không phải là chủ đề daemon chấm dứt.

Một số luồng gọi phương thức thoát của lớp Runtime hoặc Hệ thống lớp và thao tác thoát không bị cấm bởi trình quản lý bảo mật.

Điều đó có nghĩa là Bạn nên sử dụng nó khi Bạn có chương trình lớn (tốt nhất là lớn hơn chương trình này) và muốn hoàn thành việc thực hiện.


3

Nếu bạn có một chương trình khác đang chạy trong JVM và bạn sử dụng System.exit, chương trình thứ hai đó cũng sẽ bị đóng. Ví dụ, hãy tưởng tượng rằng bạn chạy một công việc java trên một nút cụm và chương trình java quản lý nút cụm đó chạy trong cùng một JVM. Nếu công việc sẽ sử dụng System.exit, nó sẽ không chỉ thoát khỏi công việc mà còn "tắt nút hoàn chỉnh". Bạn sẽ không thể gửi một công việc khác đến nút cụm đó vì chương trình quản lý đã vô tình bị đóng.

Do đó, không sử dụng System.exit nếu bạn muốn có thể điều khiển chương trình của mình từ một chương trình java khác trong cùng một JVM.

Sử dụng System.exit nếu bạn muốn đóng JVM hoàn chỉnh theo mục đích và nếu bạn muốn tận dụng các khả năng đã được mô tả trong các câu trả lời khác (ví dụ: hook hook: hook shutdown , giá trị trả về khác không cho dòng lệnh các cuộc gọi: Cách nhận trạng thái thoát của chương trình Java trong tệp bó của Windows ).

Cũng có một cái nhìn về Ngoại lệ Thời gian chạy: System.exit (num) hoặc ném RuntimeException từ chính?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.