5

I wrote like this.

public static void main(String[] args){
    Thread thread = new Thread(() -> {
        while(true){
            try{
                // do something
                Thread.sleep(10);
            }catch(InterruptedException ex){
                System.out.println("ABC");
                break;
            }
        }
    });
    JFrame frame = new JFrame();
    frame.setSize(1280,720);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter(){
        @Override
        public void windowClosed(WindowEvent e){
            thread.interrupt();
        }
    });
    thread.start();
    frame.setVisible(true);
}

When I clicked "close" button of the window,
the program took few seconds to end.
What prevents program from exiting ?
And how can I close program immediately without using JFrame.EXIT_ON_CLOSE ?

I think my thread (while(true) thread) ends immediately
because "ABC" is displayed soon after I clicked "close" button.

Thank you.

EDIT

same thing occured without my thread.

public static void main(String[] args){
    JFrame frame = new JFrame();
    frame.setSize(1280,720);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.setVisible(true);
}

EDIT2

public static void main(String[] args){
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
        Thread[] threads = new Thread[5];
        Thread.enumerate(threads);
        System.out.println(Arrays.toString(threads));
        System.err.println(LocalDateTime.now() + " SHUTDOWN");
    }
    ));
    JFrame frame = new JFrame();
    frame.setSize(1280,720);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosed(WindowEvent e){
                Thread[] threads = new Thread[5];
                Thread.enumerate(threads);
                System.out.println(Arrays.toString(threads));
                System.err.println(LocalDateTime.now() + " CLOSE");
            }
        });
    frame.setVisible(true);
}  

the output was:

[Thread[AWT-EventQueue-0,6,main], Thread[DestroyJavaVM,5,main], null, null, null]
2016-08-02T19:04:50.465 CLOSE
[Thread[DestroyJavaVM,5,main], Thread[Thread-0,5,main], null, null, null]
2016-08-02T19:04:51.762 SHUTDOWN
  • Is that your full code? Because `thread` needs to be initialized – Cir0X Aug 02 '16 at 09:15
  • I can not reproduce this. After fixing the compilation issues, `ABC` is immediately printed when clicking the close button and the program terminates. Please also review http://stackoverflow.com/documentation/swing/2266/swing-hello-world#t=201608020924423960976 to see how to properly start a Swing application. – Andreas Fester Aug 02 '16 at 09:20
  • @Cir0X sorry , the code was arranged for clarity, I edited. –  Aug 02 '16 at 09:25
  • @AndreasFester After `ABC` is printed, it took about 2 seconds to terminate. –  Aug 02 '16 at 09:28
  • Ok, that is a different story then. I added a system shutdown hook where I am printing the current time (like `Runtime.getRuntime().addShutdownHook(new Thread(() -> System.err.println(LocalDateTime.now() + " SHUTDOWN") ));` and I also added a time stamp to the output of `ABC`. I suggest that you do the same to make the issue more obvious. With that, I am observing a delay of approx. 1,5 seconds between those outputs - which, in my opinion, is not even unusual due to cleanup actions of the VM – Andreas Fester Aug 02 '16 at 09:35
  • And, btw, this is completely independant of your secondary thread. The delay also occurs (without that thread) between the `windowClosed` event and the execution of the shutdown hook. You might want to simplify your code in the question accordingly. – Andreas Fester Aug 02 '16 at 09:39
  • @AndreasFester Thank you. I edited. Before `System.out.println("ABC")` , I observed existing thread using `Thread.enumerate()` , and I observed three threads, "Thread-1" (my second thread) , "AWT-EventQueue-0" ,and "DestroyJavaVM" –  Aug 02 '16 at 09:46
  • 1
    You might want to check if https://github.com/afester/CodeSamples/blob/master/Java/Swing/src/com/example/swing/ExitDelay.java reproduces our issue. Feel free to use it as [mcve] in your question if it is useful. If yes, also add the output so that everyone is aware of the time spans you are talking about. – Andreas Fester Aug 02 '16 at 09:46
  • @AndreasFester Thank you. It took about 1.3 secs. –  Aug 02 '16 at 09:53
  • @AndreasFester "Thread-0" is shutdown hook, so the cause is "DestroyJavaVM" ... ? –  Aug 02 '16 at 10:11
  • 1
    See my comment to the answer below. The behavior you observe is documented. – Andreas Fester Aug 02 '16 at 10:12

3 Answers3

6

While System.exit() should generally be used with care, the Swing documentation explicitly states that it is the right approach in scenarios as shown in the question:

When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate. Note, however, that there can be a delay before the program exits automatically, and that under some circumstances the program might keep running. It is quicker and safer to explicitly exit the program using System.exit(int).

The following example does not show the delay between the close operation and the shutdown hook. When removing the System.exit(0) call, a delay of approx. 1,3 seconds can be observed:

package com.example.swing;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.time.LocalDateTime;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class ExitDelay {
    public static void main(String[] args){

        Runtime.getRuntime().addShutdownHook(new Thread(() -> System.err.println(LocalDateTime.now() + " SHUTDOWN") ));

        SwingUtilities.invokeLater(() -> {

            JFrame frame = new JFrame();
            frame.setSize(1280,720);
            frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            frame.addWindowListener(new WindowAdapter(){
                @Override
                public void windowClosed(WindowEvent e){
                    System.err.println(LocalDateTime.now() + " CLOSE");

                    // Do all required cleanup stuff.
                    // check that threads are done, close files, commit transactions etc.
                    // ...

                    System.exit(0);
                }
            });
            frame.setVisible(true);

        });
    }
}
Andreas Fester
  • 36,091
  • 7
  • 95
  • 123
0

To exit your program immediately you can use System.exit(0).

Like that:

public static void main(String[] args){
    Thread thread = new Thread(() -> {
        while(true){
            try{
                // do something
                Thread.sleep(10);
            }catch(InterruptedException ex){
                System.out.println("ABC");
                System.exit(0);
            }
        }
    });
    JFrame frame = new JFrame();
    frame.setSize(1280,720);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter(){
        @Override
        public void windowClosed(WindowEvent e){
            thread.interrupt();
        }
    });
    thread.start();
    frame.setVisible(true);
}
Cir0X
  • 446
  • 4
  • 11
  • sorry, I don't want to use `System.exit(0)` ,because I want to end program with my thread appropriately terminated. –  Aug 02 '16 at 09:25
  • 2
    Thats very crude and makes the use of `DISPOSE_ON_CLOSE` useless. – ArcticLord Aug 02 '16 at 09:25
  • @Shu S `System.exit(0)` doesn't look [that](http://stackoverflow.com/a/7082054/3014979) wrong in Swing – Cir0X Aug 02 '16 at 09:34
  • `System.exit` in that position is also bad, because there is another thread working. –  Aug 02 '16 at 09:35
  • @ShuS `System.exit(0)` [stops](http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#exit%28int%29) the currently running jvm – Cir0X Aug 02 '16 at 09:43
  • exactly ... and thats called "bad coding" since running threads get "killed", even if they're still writing to disk, for example – specializt Aug 02 '16 at 09:48
  • @specializt I don't think [so](http://stackoverflow.com/a/17671552/3014979) – Cir0X Aug 02 '16 at 09:51
  • you ... just gave my own claim proof. Thank you - but your actual statement (*"i dont think so")* doesnt make any sense ... – specializt Aug 02 '16 at 09:56
  • 2
    @specializt Generally I agree that `System.exit()` should be used with care. However, the [Swing documentation](https://docs.oracle.com/javase/tutorial/uiswing/events/windowlistener.html) explicitly says: `When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate. Note, however, that there can be a delay before the program exits automatically, and that under some circumstances the program might keep running. It is quicker and safer to explicitly exit the program using System.exit(int).` I would do that in the `windowClosed()` method though. – Andreas Fester Aug 02 '16 at 10:07
  • @AndreasFester Thank you very much. So what I should do is , to check all thread is appropriately terminated (with `thread.join()`) and call `System.exit` . –  Aug 02 '16 at 10:17
  • Yes. See my answer. Additionally, in a real application, I would encapsulate the termination logic in a separate method which first takes care of all required cleanup and finally calls `System.exit`. – Andreas Fester Aug 02 '16 at 10:26
0

You use DISPOSE_ON_CLOSE and that makes sense because EXIT_ON_CLOSE would use System.exit to close the jvm process with all active threads. A JFrame is a complex structure that uses some native calls to register callbacks for mouse events at your operating system. So a clean exit takes some time. Additionally the Garbage Collector needs some time to detect that the AWT-Thread that runs the JFrame is completely finish with the cleanup before he kills it. So I think 1.3 seconds is acceptable for a clean finish of your application.
But if you really want to do so you could force the end with another Thread that simply checks continuously if all running threads have finished work and all JFrames have beend disappeared. And if so it calls the big evil System.exit.


final JFrame frame = new JFrame(); // make your JFrame final
final Thread thread  = new Thread(() ...  // make your Thread final

and add this end checking thread

final Thread endChecker  = new Thread(() -> {            
    while(true){
        try{
            Thread.sleep(100);
            if(!frame.isDisplayable() && !thread.isAlive())
                System.exit(0);                 
        }catch(InterruptedException ex){
            break;
        }
    }           
});
endChecker.start();
ArcticLord
  • 3,999
  • 3
  • 27
  • 47