Ví dụ hữu ích về móc tắt máy trong Java?


125

Tôi đang cố gắng đảm bảo ứng dụng Java của mình thực hiện các bước hợp lý để trở nên mạnh mẽ và một phần trong đó liên quan đến việc tắt một cách duyên dáng. Tôi đang đọc về móc tắt máy và tôi thực sự không biết cách sử dụng chúng trong thực tế.

Có một ví dụ thực tế ngoài kia?

Giả sử tôi có một ứng dụng thực sự đơn giản như ứng dụng này bên dưới, ghi số vào một tệp, 10 đến một dòng, theo lô 100 và tôi muốn đảm bảo một lô nhất định kết thúc nếu chương trình bị gián đoạn. Tôi nhận được cách đăng ký hook shutdown nhưng tôi không biết làm thế nào để tích hợp nó vào ứng dụng của mình. Bất kỳ đề xuất?

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();
        }
    }
}

Câu trả lời:


160

Bạn có thể làm như sau:

  • Để móc tắt máy đặt một số AtomicBoolean (hoặc boolean dễ bay hơi) "keepRucky" thành false
  • (Tùy chọn, .interruptcác luồng làm việc nếu họ chờ dữ liệu trong một số cuộc gọi chặn)
  • Đợi các luồng làm việc (thực thi writeBatchtrong trường hợp của bạn) kết thúc, bằng cách gọi Thread.join()phương thức trên các luồng làm việc.
  • Chấm dứt chương trình

Một số mã sơ sài:

  • Thêm một static volatile boolean keepRunning = true;
  • Trong run () bạn đổi thành

    for (int i = 0; i < N && keepRunning; ++i)
        writeBatch(pw, i);
    
  • Trong chính () bạn thêm:

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

Đó là cách tôi thực hiện một cách "từ chối tất cả các khách hàng duyên dáng khi nhấn Control-C" trong thiết bị đầu cuối.


Từ các tài liệu :

Khi máy ảo bắt đầu trình tự tắt máy, nó sẽ bắt đầu tất cả các móc tắt đã đăng ký theo một số thứ tự không xác định và để chúng chạy đồng thời. Khi tất cả các hook đã hoàn thành, nó sẽ chạy tất cả các bộ hoàn thiện chưa được kích hoạt nếu tính năng hoàn tất khi thoát đã được bật. Cuối cùng, máy ảo sẽ dừng lại.

Đó là, một cái móc shutdown giữ JVM chạy cho đến khi móc đã chấm dứt (trở về từ run () -method.


14
À - vì vậy nếu tôi hiểu đúng, ý chính của những gì bạn đang nói là hook shutdown có cơ hội giao tiếp với các luồng hiện đang chạy khác và để tắt máy VM (ví dụ: bằng cách sử dụng Thread.join () ) cho đến khi nó hài lòng rằng việc dọn dẹp nhanh chóng đã được hoàn thành. Đó là điểm tôi đã bỏ lỡ. cảm ơn!
Jason S

2
Đúng. Bạn đã nhận nó. Cập nhật câu trả lời với một vài câu từ các tài liệu.
aioobe

1
Xin lỗi trước, nhưng GracefulShutdownTest1 không nên triển khai Runnable?
Victor

Điều này cũng sẽ làm việc một GUI đã được bắt đầu trong luồng chính? Tôi biết đó không phải là cách tốt nhất để làm điều đó (sử dụng EDT thay thế), nhưng tôi đang làm việc với mã cũ.
Agostino

1
@ MartynasJusevičius, JVM chấm dứt khi tất cả các luồng không phải daemon đã chấm dứt. Nếu mainThreadlà một luồng daemon, thì joinđảm bảo rằng chúng ta đợi nó kết thúc trước khi tắt JVM.
aioobe

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.