Contoh yang berguna dari hook shutdown di Java?

126

Saya mencoba memastikan aplikasi Java saya mengambil langkah-langkah yang wajar untuk menjadi kuat, dan bagian dari itu melibatkan mematikan dengan anggun. Saya membaca tentang shutdown hook dan saya tidak benar-benar mengerti bagaimana memanfaatkannya dalam praktik.

Apakah ada contoh praktis di luar sana?

Katakanlah saya memiliki aplikasi yang sangat sederhana seperti ini di bawah, yang menulis angka ke sebuah file, 10 ke sebuah baris, dalam batch 100, dan saya ingin memastikan batch tertentu selesai jika programnya terputus. Saya mendapatkan cara mendaftarkan hook shutdown tetapi saya tidak tahu bagaimana mengintegrasikannya ke dalam aplikasi saya. Ada saran?

package com.example.test.concurrency;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class GracefulShutdownTest1 {
    final private int N;
    final private File f;
    public GracefulShutdownTest1(File f, int N) { this.f=f; this.N = N; }

    public void run()
    {
        PrintWriter pw = null;
        try {
            FileOutputStream fos = new FileOutputStream(this.f);
            pw = new PrintWriter(fos);
            for (int i = 0; i < N; ++i)
                writeBatch(pw, i);
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        finally
        {
            pw.close();
        }       
    }

    private void writeBatch(PrintWriter pw, int i) {
        for (int j = 0; j < 100; ++j)
        {
            int k = i*100+j;
            pw.write(Integer.toString(k));
            if ((j+1)%10 == 0)
                pw.write('\n');
            else
                pw.write(' ');
        }
    }

    static public void main(String[] args)
    {
        if (args.length < 2)
        {
            System.out.println("args = [file] [N] "
                    +"where file = output filename, N=batch count");
        }
        else
        {
            new GracefulShutdownTest1(
                    new File(args[0]), 
                    Integer.parseInt(args[1])
            ).run();
        }
    }
}
Jason S
sumber

Jawaban:

160

Anda dapat melakukan hal berikut:

  • Biarkan hook shutdown mengatur beberapa AtomicBoolean (atau volatile boolean) "keepRunning" ke false
  • (Opsional, .interruptthread yang berfungsi jika menunggu data di beberapa panggilan pemblokiran)
  • Tunggu hingga thread yang berfungsi (dijalankan writeBatchdalam kasus Anda) selesai, dengan memanggil Thread.join()metode pada thread yang berfungsi.
  • Hentikan program

Beberapa kode samar:

  • Tambah sebuah static volatile boolean keepRunning = true;
  • Dalam run () Anda berubah menjadi

    for (int i = 0; i < N && keepRunning; ++i)
        writeBatch(pw, i);
  • Di main () Anda menambahkan:

    final Thread mainThread = Thread.currentThread();
    Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            keepRunning = false;
            mainThread.join();
        }
    });

Begitulah kira-kira bagaimana saya melakukan "tolak semua klien setelah menekan Control-C" di terminal.


Dari dokumen :

Ketika mesin virtual memulai urutan shutdown, itu akan memulai semua hook shutdown yang terdaftar dalam beberapa urutan yang tidak ditentukan dan membiarkannya berjalan secara bersamaan. Ketika semua hook telah selesai maka akan menjalankan semua finalizer yang tidak dipanggil jika finalization-on-exit telah diaktifkan. Akhirnya, mesin virtual akan berhenti.

Artinya, hook shutdown membuat JVM tetap berjalan sampai hook dihentikan (dikembalikan dari run () -method.

aioobe
sumber
14
Ah - jadi jika saya mengerti benar, inti dari apa yang Anda katakan adalah bahwa hook shutdown memiliki kesempatan untuk berkomunikasi dengan utas lain yang sedang berjalan, dan untuk menahan shutdown VM (misalnya dengan menggunakan Thread.join () ) hingga pembersihan cepat selesai dilakukan. Itulah poin yang saya lewatkan. Terima kasih!
Jason S
2
Iya. Anda mengerti. Memperbarui jawaban dengan beberapa kalimat dari dokumen.
aioobe
1
Maaf sebelumnya, tetapi bukankah GracefulShutdownTest1 harus mengimplementasikan Runnable?
Victor
Akankah ini juga berfungsi ketika GUI dimulai di utas utama? Saya tahu ini bukan cara terbaik untuk melakukannya (gunakan EDT sebagai gantinya), tetapi saya sedang mengerjakan kode lama.
Agostino
1
@ MartynasJusevičius, JVM berakhir ketika semua utas non-daemon telah dihentikan. Jika mainThreadadalah utas daemon, maka joinjaminan bahwa kami menunggu sampai selesai sebelum membiarkan JVM dimatikan.
aioobe