public class TestFrame extends JFrame
{
public TestFrame()
{
setBounds(10, 10, 500, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
}
public static void main(String[] args) throws InterruptedException
{
TestFrame tf = new TestFrame();
tf.add(new JButton("test1"));
tf.setVisible(true);
Thread.sleep(2000);
tf.getContentPane().removeAll();
tf.add(new JButton("test2"));
System.out.print("Show test");
}
}
I want the program show JButton("test2") after 2 seconds.
Then I add thread.sleep(2000) after test1.
But I don't know why the program stops at showing the test1 JButton,
not showing test2 JButton and the "show test" message can sucess print out
Short answer, don't.
Swing is a single threaded framework, this means that any thing that blocks the Event Dispatching Thread will prevent it from updating the UI or processing any new events (making your UI look like it's hung, cause it has).
Sure, you could use a Thread, but Swing is also not thread safe. This means that ALL modifications to the UI MUST be made from within the context of the Event Dispatching Thread. While there are ways to overcome this, the easiest way is to simply use a Swing Timer.
Take a closer look at How to use Swing Timers and Concurrency in Swing for more details
You should also take a look at Initial Threads.
When updating the UI, it may be required to call revaldiate and repaint after you have added the new components to force the UI to update to re-layout it's contents.
Related
As I tried to see if I could answer this question earlier today. I realized that I don't fully understand the Event Dispatch Thread (EDT). Googling both confirmed and helped with that and clarified why I don't. (This might also be relevant to understanding.)
The code sets up a GUI and later (as in the earlier question) updates a text field until a flag is unset.
I have several questions/requests.
Please explain why the code below runs fine if both calls (to swingInit and doIt) are outside the invokeLater block (as shown), since both calls affect or query the GUI yet neither are executing on the EDT (are they?). Isn't that inviting failure?
Code also runs if call to swingInit is inside and doIt outside invokeLater. So swingInit is executed on the EDT, but shouldn't doIt not executing on the EDT be a problem? (I was surprised that this worked. Should I have been?)
I guess I understand why it hangs if doIt is inside invokeLater regardless of where swingInit is: the purpose of invokeLater is ONLY to initialize the GUI (right?).
Should doIt only be initiated (possibly from an event occurring) on the EDT but certainly not inside invokeLater block?
(The history of the EDT concept is interesting. It was not always thus. See link above to "why I don't" understand it.)
import static java.awt.EventQueue.invokeLater;
import java.awt.event.*;
import javax.swing.*;
public class Whatever
{
static boolean flag = true;
static JTextField tf = new JTextField("Hi",20);
static JPanel p = new JPanel();
static JFrame f = new JFrame();
static JButton b = new JButton("End");
public static void main(String[] args)
{
swingInit();
invokeLater
(
new Runnable()
{
#Override
public void run()
{
// swingInit();
// doIt();
}
}
);
doIt();
}
static void swingInit()
{
b.addMouseListener
(
new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
flag = false;
JOptionPane.showMessageDialog(null,"Clicked... exiting");
System.exit(0);
}
}
);
p.add(tf);
p.add(b);
f.add(p);
f.setVisible(true);
f.pack();
f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
static String getInfo(){
return "Hello... " + Math.random();
}
static void doIt(){
while(flag)
tf.setText(getInfo());
};
}
Taking each of your bullet points:
The code is launched on the Main thread - the EDT is run in parallel.
swingInit returns after constructing the UI which is then under
control of the EDT, allowing dotIt to do its thing in parallel on the main thread
Similar situation as above, but here you are guaranteeing construction of the UI on the EDT (as recommended by Oracle).
A long running task is placed onto the EDT, preventing it from showing (if placed before swingIt) or painting and interaction (if placed after). the purpose of invokeLater is ONLY to initialize the GUI The purpose is to place non-thread safe Swing calls onto the EDT. If within the main method, I would recommend using SwingUtilities.invokeAndWait
If you wish to update the UI like this, consider doing so with a SwingTimer.
Running EDT specific, non-thread safe code outside the EDT doesn't guarantee failure, but it does invite failure (via conflicts when two (or more) threads attempt to update data at the same time).
I once spent hours tracking down a mysterious NullPointerException, only to realize it was a LookAndFeel issue whose calls were not on the EDT. Lesson learned.
static void doIt(){
while(flag)
tf.setText(getInfo());
};
That busy-loop inside doIt ties up the GUI thread (spinning in that loop), which will cause the GUI to hang.
You didn't actually explain what you mean by "runs fine", but I'm assuming that's the problem that you're seeing.
You might want to use a Swing Timer to do something like what you do in the loop.
If I understand correctly then when I create GUI swing components, for example I have this:
public class frameExample extends JFrame{
public frameExample(){
//Here adding bunch if components
setVisible(true);
}
}
So as long as I don't call the setVisible method the components are being made from the thread the instance was created. So if in a class where i have my main method write:
JFrame test=new frameExample();
and I sysout
Thread.currentThread.getName();
in the constructor in frameExample just before setVisible I should get: main.
After that the responsibility of creating and maintaining swing elements are passed to the event-dispatch-thread and because it is not thread safe every component add/remove/modify should be done within the EDT thread.
So I should place my setVisible as the last line of code in my constructor, or call it separately.
As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.
Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.
So here is why I'm confused.
I made this code:
public class GUI extends JFrame {
JButton btn = new JButton("Change");
JMenuBar m = new JMenuBar();
public GUI() {
super("Test");
setSize(400, 400);
setDefaultCloseOperation(EXIT_ON_CLOSE);
m.add(new JMenu("menu"));
add(m, BorderLayout.NORTH);
add(btn, BorderLayout.SOUTH);
System.out.println("Current thread: before setVisible "+Thread.currentThread().getName());
setVisible(true);
System.out.println("Current thread: after setVisible "+Thread.currentThread().getName());
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
add(new JButton("testbtn1"), BorderLayout.EAST);
add(new JButton("testbtn2"));
System.out.println("Current thread: "+Thread.currentThread().getName());
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 1E8; i++) {
Math.sin(5.0);
}
System.out.println("Current thread: "+Thread.currentThread().getName());
}
}).start();
}
});
}
}
So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it (which forces the edt to update its components??? what is the reason for this?).
So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.
So my first question is: why my components are not updated within edt and why they are visible after resize
second one: why can i make changes to swing components outside edt and everything works fine? Is this just random thread behvior where for example things just work fine without sync block but when you rerun you program at some point it will eventually crash because the lack of sync.
So as long as I don't call the setVisible method the components are being made from the thread the instance was created
Wrong. As long as you do not specifically create a new Thread or use a utility method (like for example the SwingUtilities#invoke... methods), each call is invoked on the current Thread, including the setVisible call.
It looks like you think that making Swing components visible somehow makes your code switch threads. No, the painting of the Swing components will happen on the EDT. And as you correctly stated, Swing is not thread safe.
That is why you should create the component on the EDT as well, and not on another thread. It might work without problems the majority of the time, but eventually you will stumble upon weird bugs.
As I understand all event listening goes through the EDT. So if I create a new component within for example an actionPerformed method it should do fine.
Also if I pass a runnable instance to invokeLater or invokeAndWait then all the run() method will be done by the EDT.
Correct.
So in my anonim class where I add the two buttons im in the EDT, but the components are not added to my frame just after i resize it
Adding components to a Container requires you to revalidate the layout (see the javadoc of the Container#add method). Just call
revalidate();
repaint();
after you add the buttons and it will work as expected. Manually resizing the frame has the same effect as you already noticed.
So i dont get my new components even within edt, however when i create a random thread outside edt and make it change some gui element property (for example setText) in just works fine outside the edt.
As stated before, it might work most of the time, but there is no guarantee it will work 100% of the time. The reason the adding of your components did not work as expected is explained above, and has nothing to do with threading issues.
I am very curious why we have to use java.awt.EventQueue.invokeLater to control swing components.
Why can't we do that in a normal thread? What exactly is going on behind the scenes? From what I have noticed if I have a JFrame I can set visibility to true or false from the main thread without getting any errors, and it does seem to work. So what exactly do I achieve by using java.awt.EventQueue.invokeLater? I am also fully aware that I can use SwingUtilities.invokeLater but as explained here, they seem to be one and the same thing.
Thanks to anyone for their explanation. Hopefully this is a valid question.
EDIT: to answer wumpz question
We can create a jframe
JFrame frame = new JFrame("Hello world");
frame.setSize(new Dimension(300, 300));
frame.setPreferredSize(new Dimension(300, 300));
frame.setMaximumSize(new Dimension(300, 300));
frame.setMinimumSize(new Dimension(300, 300));
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
And on the same thread it was created do the following.
for (int i = 0; i < 34; i++)
{
System.out.println("Main thread setting to "+(!frame.isVisible()));
frame.setVisible(!frame.isVisible());
}
And no complaints.
The complete Swing processing is done in a thread called EDT (Event Dispatching Thread). Therefore you would block the GUI if you would compute some long lasting calculations within this thread.
The way to go here is to process your calculation within a different thread, so your GUI stays responsive. At the end you want to update your GUI, which have to be done within the EDT. Now EventQueue.invokeLater comes into play. It posts an event (your Runnable) at the end of Swings event list and is processed after all previous GUI events are processed.
Also the usage of EventQueue.invokeAndWait is possible here. The difference is, that your calculation thread blocks until your GUI is updated.
So it is obvious that this must not be used from the EDT.
Be careful not to update your Swing GUI from a different thread. In most cases this produces some strange updating/refreshing issues.
Still there is Java code out there that starts a JFrame simple from the main thread. This could cause issues, but is not prevented from Swing. Most modern IDEs now create something like this to start the GUI:
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
All supported platforms offer single-threaded graphics libraries. Swing is cross-platform. Therefore, Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
As an aside, SwingUtilities.invokeLater() is a cover for EventQueue.invokeLater() since version 1.3.
I have a JLabel which I want to change momentarily, here is the code I have written to do so:
infoLabel.setText("Added");
try {
TimeUnit.MILLISECONDS.sleep(300);
}
catch(InterruptedException ex)
{
}
infoLabel.setText("Action"); //funny part is when I comment this line it works
My default text for the label is 'Action'
Swing is a single threaded frame work, that means, if you do anything that stops this thread, then it can't respond to any new events, including paint requests.
Basically, TimeUnit.MILLISECONDS.sleep(300) is causing the Event Dispatching Thread to be put to sleep, preventing it from processing any new paint requests (amongst other things).
Instead, you should use a javax.swing.Timer
Take a look at
Concurrency in Swing
How to use Swing Timers
For more details
For example...
infoLabel.setText("Added");
Timer timer = new Timer(300, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
infoLabel.setText("Action");
}
});
timer.setRepeats(false);
timer.start();
Note, 300 milliseconds is a really short time, you might like to start with a value a little larger like 2000, which is 2 seconds ;)
You're sleeping the Swing event thread putting the entire GUI to sleep. Don't do that. Use a Swing Timer instead.
Your application is run on a single thread, so when you sleep the thread, you prevent it from making any GUI updates.
Are you sure you are doing things properly? By doing everything (including sleep) in the GUI thread, it will always be busy and never get back to Java in order to let the GUI be redrawn.
Search for EDT (Event dispatch thread) for more info. Here is one question on the subject: Processing code doesn't work (Threads, draw(), noLoop(), and loop())
I'm just getting to grips with GUI programming in java. Here is a trivial program (from O'Reilly's "Head First Java") which on the face of it looks easy to understand, but there's an aspect of it which I don't follow.
import javax.swing.*;
public class Test {
public static void main(String[] args) {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
}
}
This simple program, when compiled and run, will open a window with a button on it.
What I don't understand is what is happening with the flow of execution. When I run this program, the static main method of the Test class runs, all the commands in main() are executed -- so why doesn't the process terminate after the window appears? Why am I still sitting on what looks like an infinite loop? What is looping?
If I add the line
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
then I find the result even more imcomprehensible. Now, of course, the program terminates
once I've closed the window. But again I don't see why. The frame will be on the stack but I don't see where the program flow is and just the existence of something on the stack is not enough to keep the program alive, surely? I'm missing something fundamental which as far as I can see is not covered in the book I'm reading. I am slightly surprised by this -- "Head first Java" has been very good up until now at pointing out subtleties and explaining what is really going on, but doesn't seem to address this point (at least not that I've spotted).
why doesn't the process terminate after the window appears?
Because the Java Virtual Machine exits only after all non-daemon threads have finished. While not apparent, there's in fact two threads in your program: the main thread, and the event dispatching thread, which does everything related to the Swing GUI components. The event dispatching thread keeps going as long as any GUI components are visible.
Actually the program, while it may work, is wrong, because you're creating and accessing Swing components from the main thread. You ought to be doing all GUI work in the event dispatching thread. That is, it should be something like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
JFrame frame=new JFrame();
JButton button = new JButton("click me");
frame.getContentPane().add(button);
frame.setSize(300,300);
frame.setVisible(true);
});
}
The Java process terminates when the last non-demon thread dies. Normally there is just one, the main thread. When you display Swing components additional non-demon threads for event dispatching and GUI shotdown are started. Those terminate when the last top-level component gets disposed. In your sample the main thread dies after leaving the main method. You can have a look into the threads with a debugger or jvisualvm from the JDK tools.
The rest of the GUI flow is event driven. When you e.g. click on the frame's close button an event is generated and sent to the appropriate listeners within the event dispatching thread.
Setting JFrame.EXIT_ON_CLOSE as default close operation is like adding a default event listener to the frame. A quite harsh one, it just shuts down the JVM without respect to the rest of the application's state.