Как JVM завершает потоки демона?или Как писать потоки демонов, которые завершаются изящно - PullRequest
28 голосов
/ 29 декабря 2011

Гипотетический сценарий:
У меня есть поток демона, отвечающий за некоторые операции ввода-вывода, основной поток завершает и возвращает, а JVM решает прекратить мой поток демона.

Как это сделать? Прерывание? Доработка? Как я могу закодировать мой поток демона, чтобы он корректно реагировал на завершение?

Ответы [ 4 ]

11 голосов
/ 29 декабря 2011

Я только что написал следующий код в качестве теста:

public class DaemonThreadPlay {
    public static void main(String [] args) {
        Thread daemonThread = new Thread() {
            public void run() {
                while (true) {
                    try {
                        System.out.println("Try block executed");
                        Thread.sleep(1000l);
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                }
            }

            @Override
            public void finalize() {
                System.out.println("Finalize method called");
            }
        };
        daemonThread.setDaemon(true);
        daemonThread.start();

        try {
            Thread.sleep(2500l);
        } catch (Throwable t) {
            //NO-OP
        }
    }
}    

Я установил точки останова в блоке catch потока демона и в методе finalize. Ни одна точка останова не была достигнута, хотя блок try был выполнен. Очевидно, у этого кода есть проблемы с синхронизацией / синхронизацией, но я думаю, что мы можем с уверенностью заключить, что потоки демона не прерываются при завершении работы и не обязательно вызывается их методы finalize ().

Вы всегда можете добавить хук отключения к JVM Runtime:

Thread shutdownHook = ... // construct thread that somehow
                          // knows about all the daemon threads
Runtime.getRuntime().addShutdownHook(shutdownHook);

Ваш хук отключения, очевидно, может выполнять любые задачи, необходимые для "изящного" отключения.

5 голосов
/ 29 августа 2013

Я думаю, вы неправильно понимаете, что такое поток демона.

См. Что такое поток демона в java

В целом, это в основном означает, что поток демона не долженне делать никаких операций ввода-вывода или удерживать какие-либо ресурсы.Если вы нарушаете это основное правило, то ваш поток не может считаться потоком демона.

Добавление перехватчика завершения работы - это стандартный способ обеспечения того, чтобы ваш код вызывался до завершения JVM, но даже это не гарантируется на 100% - например, ваша JVM может аварийно завершить работу, что приведет к тому, что ОС приведет к очистке ресурсовспособ, который защищает ОС, но вполне вероятно оставляет ваше приложение в несогласованном / ошибочном состоянии.

Системные контрольные точки и механизмы восстановления восходят к ранним дням разработки программного обеспечения (например, к операционным системам и пакетным операциям), и, к сожалению, это колесо продолжает изобретаться заново, так как не существует подхода «серебряной пули» (API)которая решает эту проблему достаточно общим образом.

4 голосов
/ 29 декабря 2011

AFAIK, потоки Daemon не предназначены для работы основного потока ввода / вывода.Если все потоки завершены, JVM может внезапно закрыть все потоки демона.Возможное решение для вашего требования - создать ExecutorService, как показано ниже:

ExecutorService execPool = Executors.newSingleThreadExecutor(new ThreadFactory() {

    @Override    
    public Thread newThread(Runnable runnable) {       
         Thread thread = Executors.defaultThreadFactory().newThread(runnable);
         thread.setDaemon(true);
         return thread;    
    } 
}); 

вызовите метод завершения executorservice из ловушки Shutdown.

Runtime.getRuntime().addShutdownHook(....)
1 голос
/ 29 декабря 2011

используйте прерывание и присоединитесь:

class Daemon extends Thread {
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println(e);
                break;
            }
        }
        System.out.println("exit run()");
    }
}   
public class So8663107 {
    public static void main(String[] arguments) throws InterruptedException {
        System.out.println(Thread.activeCount());
        Daemon daemon = new Daemon();
        daemon.setDaemon(true);
        daemon.start();
        Thread.sleep(2500);
        System.out.println(Thread.activeCount());
        daemon.interrupt();
        daemon.join();
        System.out.println(Thread.activeCount());
    }
}
...