Kapan kita harus memanggil System.exit di Jawa

193

Di Jawa, Apa bedanya dengan atau tanpa System.exit(0)dalam kode berikut?

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

The Dokumen mengatakan: "Metode ini tidak pernah kembali normal." Apa artinya?

pierrotlefou
sumber

Jawaban:

207

System.exit()dapat digunakan untuk menjalankan kait shutdown sebelum program berhenti. Ini adalah cara yang mudah untuk menangani shutdown dalam program yang lebih besar, di mana semua bagian dari program tidak dapat (dan tidak seharusnya) saling memperhatikan. Kemudian, jika seseorang ingin berhenti, dia bisa langsung meneleponSystem.exit() , dan kait penghenti (jika diatur dengan benar) mengurus semua upacara penutupan yang diperlukan seperti menutup file, melepaskan sumber daya dll.

"Metode ini tidak pernah kembali secara normal." hanya berarti bahwa metode tersebut tidak akan kembali; sekali utas masuk ke sana, tidak akan kembali.

Cara lain, mungkin lebih umum, untuk keluar dari suatu program adalah dengan hanya mencapai akhir mainmetode. Tetapi jika ada utas non-daemon berjalan, mereka tidak akan dimatikan dan dengan demikian JVM tidak akan keluar. Jadi, jika Anda memiliki utas non-daemon semacam itu, Anda memerlukan cara lain (selain kait penonaktifan) untuk mematikan semua utas non-daemon dan melepaskan sumber daya lainnya. Jika tidak ada utas non-daemon lainnya, kembali darimain akan mematikan JVM dan akan memanggil kait penonaktifan.

Untuk beberapa alasan kait penghentian tampaknya merupakan mekanisme yang undervalued dan disalahpahami, dan orang-orang menciptakan kembali roda dengan semua jenis peretasan khusus untuk menghentikan program mereka. Saya akan mendorong menggunakan kait shutdown; itu semua ada di Runtime standar yang akan Anda gunakan.

Joonas Pulakka
sumber
10
"Metode ini tidak pernah kembali secara normal." hanya berarti bahwa metode tersebut tidak akan kembali; sekali utas masuk ke sana, tidak akan kembali. Perhatikan khususnya bahwa ini berarti Anda tidak dapat menguji unit metode yang membuat panggilan System.exit (0) ...
Bill Michell
5
-1 Salah. Shutdown hooks akan berjalan jika JVM berakhir secara normal, tidak peduli apakah itu karena System.exit atau terminasi main (). Lihat zx81 / doku / java / javadoc / j2se1.5.0 / docs / api / java / lang /…
sleske
31
@sleske: Pengakhiran main () tidak memadai jika ada utas non-daemon lain di sekitarnya. Shutdown dimulai hanya setelah utas non-daemon terakhir berakhir, kecuali jika Anda secara eksplisit memanggil System.exit (). Itu dengan jelas dinyatakan dalam dokumentasi Runtime.
Joonas Pulakka
3
Perhatikan bahwa jika hook shutdown Anda bergantung pada utas yang disebut System.exit, Anda akan menemui jalan buntu.
djechlin
8
Sesuatu untuk ditambahkan, jika seseorang menghentikan runtime, kait penonaktifan tidak akan berjalan ( Runtime.getRuntime().halt())
Rogue
50

Dalam hal ini, itu tidak diperlukan. Tidak ada utas tambahan yang telah dimulai, Anda tidak mengubah kode keluar (yang standarnya 0) - pada dasarnya tidak ada gunanya.

Ketika dokumen mengatakan metode tidak pernah kembali secara normal, itu berarti baris kode berikutnya secara efektif tidak dapat dijangkau, meskipun kompiler tidak tahu bahwa:

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

Entah pengecualian akan dilemparkan, atau VM akan berakhir sebelum kembali. Itu tidak akan pernah "hanya kembali".

Sangat jarang System.exit()dipanggil IME. Masuk akal jika Anda menulis alat baris perintah, dan Anda ingin menunjukkan kesalahan melalui kode keluar daripada hanya melemparkan pengecualian ... tapi saya tidak ingat kapan terakhir kali saya menggunakannya dalam kode produksi normal .

Jon Skeet
sumber
2
Mengapa kompiler tidak mengetahui hal itu? Bukankah System.exit () cukup istimewa untuk menjamin kode deteksi spesifik?
Bart van Heukelom
3
@ Bart: Tidak, kurasa tidak. Menempatkan kasus khusus dalam bahasa untuk hal-hal seperti ini menambah kompleksitas bahasa dengan manfaat yang sangat sedikit.
Jon Skeet
Saya pikir "tidak pernah kembali secara normal" ada hubungannya dengan "penyelesaian tiba-tiba" dari pernyataan itu. (JLS bagian 14.1). Apakah aku salah?
aioobe
@aioobe: Tidak, kamu benar. System.exit()tidak akan pernah selesai secara normal - itu akan selalu lengkap dengan pengecualian, atau dengan VM dimatikan (yang tidak benar-benar tercakup dalam JLS).
Jon Skeet
1
@ Piechuckerr: Apa yang membuatmu berpikir begitu? Sangat masuk akal untuk menyebutnya dengan nilai selain 0, untuk menunjukkan ke shell panggilan (atau apa pun) bahwa program telah mengalami kesalahan.
Jon Skeet
15

Metode tidak pernah kembali karena ini adalah akhir dunia dan tidak ada kode Anda yang akan dieksekusi selanjutnya.

Aplikasi Anda, dalam contoh Anda, akan tetap keluar di tempat yang sama dalam kode, tetapi, jika Anda menggunakan System.exit. Anda memiliki opsi untuk mengembalikan kode khusus ke lingkungan, seperti, katakanlah

System.exit(42);

Siapa yang akan menggunakan kode keluar Anda? Sebuah skrip yang disebut aplikasi. Bekerja di Windows, Unix dan semua lingkungan skrip lainnya.

Mengapa mengembalikan kode? Untuk mengatakan hal-hal seperti "Saya tidak berhasil", "Basis data tidak menjawab".

Untuk melihat cara mendapatkan nilai dari kode keluar dan menggunakannya dalam skrip unix shell atau skrip windows cmd, Anda dapat memeriksa jawaban ini di situs ini

mico
sumber
14

System.exit(0)mengakhiri JVM. Dalam contoh sederhana seperti ini sulit untuk mempersepsikan perbedaannya. Parameter dilewatkan kembali ke OS dan biasanya digunakan untuk menunjukkan penghentian abnormal (misalnya beberapa jenis kesalahan fatal), jadi jika Anda memanggil java dari file batch atau skrip shell Anda akan bisa mendapatkan nilai ini dan mendapatkan ide apakah aplikasi itu berhasil.

Ini akan membuat dampak yang cukup besar jika Anda memanggil System.exit(0)aplikasi yang digunakan ke server aplikasi (pikirkan sebelum Anda mencobanya).

Qwerky
sumber
12

Dalam aplikasi yang mungkin memiliki kait shutdown yang kompleks, metode ini tidak boleh dipanggil dari utas yang tidak dikenal. System.exittidak pernah keluar secara normal karena panggilan akan diblokir sampai JVM diakhiri. Seolah-olah kode apa pun yang berjalan yang steker listriknya dicabut sebelum bisa selesai. Memanggil System.exitakan memulai kait penutupan program dan utas apa pun yang panggilan System.exitakan diblokir sampai penghentian program. Ini memiliki implikasi bahwa jika hook shutdown pada gilirannya mengirimkan tugas ke utas dari mana System.exitdipanggil, program akan menemui jalan buntu.

Saya menangani ini dalam kode saya dengan yang berikut:

public static void exit(final int status) {
    new Thread("App-exit") {
        @Override
        public void run() {
            System.exit(status);
        }
    }.start();
}
Djechlin
sumber
Saya juga memiliki masalah di mana System.exit tidak akan benar-benar mengakhiri JVM. Namun itu hanya terjadi dalam kondisi tertentu. Threadump yang saya ambil tidak memberi saya wawasan tentang penangan utas / shutdown mana yang memblokir shutdown. Menggunakan kode yang dijelaskan dalam komentar membantu saya menyelesaikannya!
Kai
7

Meskipun jawabannya benar-benar membantu tetapi beberapa cara itu melewatkan beberapa detail tambahan. Saya harap di bawah ini akan membantu memahami proses shutdown di java, selain jawaban di atas:

  1. Dalam suatu shutdown tertib *, JVM pertama kali memulai semua kait shutdown terdaftar. Shutdown hooks adalah utas yang belum dimulai yang terdaftar di Runtime.addShutdownHook.
  2. JVM tidak memberikan jaminan pada urutan di mana kait shutdown dimulai. Jika ada utas aplikasi (daemon atau nondaemon) yang masih berjalan pada saat shutdown, mereka terus berjalan secara bersamaan dengan proses shutdown .
  3. Ketika semua kait penonaktifan telah selesai, JVM dapat memilih untuk menjalankan finalizers jika runFinalizersOnExit benar, dan kemudian berhenti.
  4. JVM tidak berusaha untuk menghentikan atau menginterupsi utas aplikasi yang masih berjalan pada saat shutdown; mereka tiba-tiba dihentikan ketika JVM akhirnya berhenti.
  5. Jika shutdown hook atau finalizer tidak selesai, maka proses shutdown yang tertib "hang" dan JVM harus dimatikan tiba-tiba.
  6. Dalam shutdown mendadak, JVM tidak diharuskan melakukan apa pun selain menghentikan JVM; kait shutdown tidak akan berjalan.

PS: JVM dapat dimatikan dengan tertib atau tiba - tiba .

  1. Pematian tertib dimulai ketika utas “normal” (nondaemon) terakhir berakhir, seseorang memanggil System.exit, atau dengan sarana khusus platform lainnya (seperti mengirim SIGINT atau menekan Ctrl-C).
  2. Walaupun di atas adalah cara standar dan disukai untuk JVM untuk mematikan, itu juga dapat ditutup secara tiba-tiba dengan memanggil Runtime.halt atau dengan membunuh proses JVM melalui sistem operasi (seperti mengirim SIGKILL).
John
sumber
7

Orang TIDAK PERNAH harus menelepon System.exit(0)untuk alasan ini:

  1. Ini adalah "goto" dan "goto" tersembunyi yang memecah aliran kontrol. Mengandalkan kait dalam konteks ini adalah pemetaan mental yang harus disadari setiap pengembang dalam tim.
  2. Keluar dari program "secara normal" memberikan kode keluar yang sama ke sistem operasi karena System.exit(0)itu berlebihan.

    Jika program Anda tidak dapat berhenti "secara normal" Anda telah kehilangan kendali atas [desain] pengembangan Anda. Anda harus selalu memiliki kontrol penuh terhadap status sistem.

  3. Masalah pemrograman seperti menjalankan utas yang tidak berhenti biasanya menjadi tersembunyi.
  4. Anda mungkin mengalami keadaan aplikasi yang tidak konsisten mengganggu utas secara tidak normal. (Lihat # 3)

Ngomong-ngomong: Mengembalikan kode pengembalian lain dari 0 masuk akal jika Anda ingin menunjukkan penghentian program yang tidak normal.

oopexpert
sumber
2
"Kamu harus selalu memiliki kendali penuh atas status sistem." Itu tidak mungkin di Jawa. Kerangka kerja IoC dan server aplikasi hanya 2 cara yang sangat populer dan banyak digunakan Anda menyerahkan kendali Anda terhadap status sistem. Dan itu sama sekali tidak masalah.
mhlz
Silakan jalankan System.exit (0) di server aplikasi Anda dan ceritakan tentang reaksi admin server Anda ... Anda melewatkan subjek pertanyaan ini dan jawaban saya.
oopexpert
Mungkin saya tidak cukup jelas. Anda harus selalu memiliki kendali penuh atas status sistem yang menjadi tanggung jawab Anda. Ya, beberapa tanggung jawab dialihkan ke server aplikasi dan IoC. Tetapi saya tidak membicarakan hal itu.
oopexpert
5

Diperlukan System.exit

  • ketika Anda ingin mengembalikan kode kesalahan non-0
  • ketika Anda ingin keluar dari program Anda dari suatu tempat yang bukan main ()

Dalam kasus Anda, ia melakukan hal yang persis sama dengan pengembalian-dari-main yang sederhana.

mfx
sumber
Apa yang Anda maksud dengan yang terakhir? Cara yang tepat untuk mematikan program adalah menghentikan semua utas (baik), suatu langkah yang tidak harus dimulai dari utas utama, jadi tidak seperti exitini adalah satu-satunya pilihan Anda di sana.
Bart van Heukelom
@ Bart: apa yang Anda gambarkan adalah salah satu cara yang mungkin untuk mematikan suatu program, bukan cara yang tepat . Tidak ada yang salah dalam menggunakan kait shutdown untuk menghentikan semua utas dengan baik. Dan kait shutdown diluncurkan dengan exit. Bukan satu-satunya pilihan, tetapi jelas opsi yang layak dipertimbangkan.
Joonas Pulakka
Tetapi apa yang saya kumpulkan dari dokumen, kait penutup sangat rumit dan mungkin tidak punya banyak waktu untuk melakukan apa yang Anda inginkan. Dengan segala cara, gunakan mereka untuk berhenti dengan baik jika terjadi shutdown OS atau sesuatu, tetapi jika Anda akan memanggil System.exit () sendiri, saya pikir lebih baik melakukan kode shutdown sebelumnya (setelah itu Anda mungkin tidak akan perlu System.exit () lagi)
Bart van Heukelom
1
Shutdown hooks memiliki semua waktu yang diperlukan untuk menyelesaikannya. VM tidak dihentikan sebelum semua kait kembali. Memang mungkin untuk mencegah VM berhenti dengan mendaftarkan hook shutdown yang membutuhkan waktu sangat lama untuk dieksekusi. Tetapi tentu saja dimungkinkan untuk melakukan urutan shutdown dengan berbagai cara alternatif.
Joonas Pulakka
Kembali dari main tidak akan ditutup jika ada utas non-daemon lainnya.
djechlin
4

Spesifikasi Bahasa Jawa mengatakan itu

Program Keluar

Suatu program menghentikan semua aktivitasnya dan keluar ketika salah satu dari dua hal terjadi:

Semua utas yang bukan daemon utas berakhir.

Beberapa utas memanggil metode keluar dari kelas Runtime atau Sistem kelas , dan operasi keluar tidak dilarang oleh manajer keamanan.

Itu berarti bahwa Anda harus menggunakannya ketika Anda memiliki program besar (baik, jangan sampai lebih besar dari ini) dan ingin menyelesaikan eksekusi.

Marcin Szymczak
sumber
3

Jika Anda memiliki program lain yang berjalan di JVM dan Anda menggunakan System.exit, program kedua itu akan ditutup juga. Bayangkan misalnya Anda menjalankan pekerjaan java pada node cluster dan bahwa program java yang mengelola node cluster berjalan di JVM yang sama. Jika pekerjaan itu akan menggunakan System.exit itu tidak hanya akan berhenti dari pekerjaan tetapi juga "mematikan node lengkap". Anda tidak akan dapat mengirim pekerjaan lain ke cluster node itu karena program manajemen telah ditutup secara tidak sengaja.

Oleh karena itu, jangan gunakan System.exit jika Anda ingin dapat mengontrol program Anda dari program java lain dalam JVM yang sama.

Gunakan System.exit jika Anda ingin menutup JVM lengkap dengan sengaja dan jika Anda ingin memanfaatkan kemungkinan yang telah dijelaskan dalam jawaban lain (mis. Kait penutup: kait penutup Java , kaitkan nilai pengembalian nol untuk baris perintah panggilan: Cara mendapatkan status keluar dari program Java di file batch Windows ).

Juga lihat Pengecualian Runtime: System.exit (num) atau melempar RuntimeException dari main?

Stefan
sumber