Thread.sleep() not working as it should [duplicate] - java

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

Java usage of revalidate() and repaint() not properly working

So I tried creating a frame to perform a Diashow, but for some reason, I can see the only an empty frame and after everything is done, I see the last picture. What I wanted to see was each picture with a ~1 second pause in between. I used revalidate() / repaint() the way I suppose it is working, but Im fairly certain that the problem is there, since I cant think of another reason.
I am only starting to learn Java.Swing, so any input is really welcome to improve my skills. As I already said, I assume that the problem lies with my usage of revalidate(), but I cant fix it alone with google..
As input to the class I use an array of BufferedImage,for which I want to create the diashow.
I also tried putting the images directly on my Container c instead of on the JPanel p, but it doenst work either way I intend it to work.
public class DiashowFrame extends JFrame {
Container c;
JPanel p;
public DiashowFrame(JFrame father,BufferedImage [] image) {
c= getContentPane();
c.setLayout(new FlowLayout());
p = new JPanel();
p.setLayout(new FlowLayout());
p.setSize(500,500);
c.add(p);
setSize(500,500);
setLocation(father.getX(),father.getY());
setVisible(true);
dia(p,image);
}
public static void dia(JPanel p,BufferedImage[] image) {
JLabel def= new JLabel(new ImageIcon(image[0]));
p.add(def);
//c.repaint();
p.revalidate();
for(int x=1;x<image.length;x++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//p.removeAll();
//p.revalidate();
Image imager = image[x].getScaledInstance(500, 500, 100);
def = new JLabel(new ImageIcon(imager));
p.add(def);
p.revalidate();
//p.repaint();
}
}
}
Start by taking a look at Concurrency in Swing.
Swing, like most GUI frameworks, is single threaded AND not thread safe.
This means that any long running or blocking operation executed from within the Event Dispatching Thread will prevent the EDT from processing the Event Queue and update the UI in anyway.
While you could use a Thread to offload the wait time to a second thread, Swing is NOT thread safe, meaning you should never update/modify the UI directly or indirectly from outside the context of the EDT.
The simplest solution in your case is to simply use a Swing Timer. This allows you to specify a delay between updates (and if it's repeating or not), which is executed off the EDT, but when triggered, is notified within the context of the EDT, making easy and safe to use with Swing.
The Timer acts as a pseudo loop, each trigger of the Timer representing the next iteration
See How to use Swing Timers for more details

JInternalFrame won't appear

I have a java class called GameUpdater which extends JInternalFrame.
It used to extend JFrame when I ran the class as a program by itself, but I changed it to JInternalFrame to become part of a larger application - now accessible from a menu button.
The function being called when I press this menu button is as follows:
private void update(){
GameUpdater gu = new GameUpdater();
desktop.add(gu); //add to JDesktopPane
gu.setSize(400, 300);
gu.setVisible(true);
gu.readMatches();//this function takes ages
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
gu.setMatch("DONE"); //now it shows the frame, way too late
}
The method gu.readMatches() takes a long time to execute, so it periodically updates content in the JInternalFrame to display its progress. However the frame is not being shown until this update function is complete!
It's like setVisible(true) is waiting until the end of the function...
It worked absolutely fine when it was a JFrame. Is there any weird property of a JInternalFrame that would cause this?
Cheers
It sounds like you're executing a time consuming process inside the Event Dispatching Thread (EDT), this will prevent the event queue from process (amongst other things) repaint requests.
This will cause you program to appear as if it has "hung".
You need to off load this task to a background thread.
Have a read through Concurrency in Swing, especially the section on Worker Threads and SwingWorker
The problem is that you are blocking your EDT this can be taken care of by simply creating a new Thread/Runnable thar calls gu.readMatches(); the method:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
gu.readMatches(); //gu will have to be declared `final`
gu.setMatch("Updating database...");//this is some output to the user, displays info in the internal frame
//try and insert into database
for(Match m : gu.getMatches()){
db.insertMatch(m);
}
}
});
ofcourse though you might want to implement a JProgressBar so the user can keep track of of how far the reading is.

Lag before JFrame event handlers are added?

I'm working on a simple Java swing project. This is the code of the main class (name changed):
public class MainProg
{
private static MainProg program;
//mainWin is a JFrame
private MainWindow mainWin;
//Event handler class which extends MouseAdapter
private TrayManager trayMgr;
public static void main(String[] args)
{
program = new MainProg();
}
public MainProg()
{
mainWin = new MainWindow();
trayMgr = new TrayManager();
mainWin.startBtn.addMouseListener(trayMgr);
mainWin.setVisible(true);
}
}
As is clear, when the program starts, in main() it creates a new instance of the MainProg class, which then calls the constructor. In the constructor, it creates a new instance of the JFrame mainWin. It then attaches an event handler to a button on mainWin.
In the event handler class trayMgr, the only method is mouseClicked() which does nothing
except a System.out.println('Clicked');
The issue is, when I run this program in Netbeans, the JFrame is shown right away, but I seem to have to click the button 2-3 times before the message is printed in the console.
Is this just something specific to Netbeans, or do I have to change something to make the event handler be set before the window is made visible?
Your threading issue is not likely one that is causing your current problem, but there's the theoretic potential for problems, and I've seen some real problems associated with some of the more touchy look and feels. Quite simply you should queue your code that starts your GUI onto the Swing event thread. You do this by doing:
public void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(
public void run() {
program = new MainProg();
}
));
}
Someone else recommended using invokeAndWait(...) instead of invokeLater(...) but this can be risky especially if you inadvertently make this call from within the Swing event thread itself. For your situation you're better off using invokeLater(...).
But again, I think the main problem with the code you have shown was inappropriate use of MouseListener where an ActionListener should have been used. Learning to code any GUI library can be quite tricky, and for that reason, you can't assume anything. Check out the tutorials and learn from the experts. Also if you are considering coding Swing for the long haul, consider ditching the NetBean's code-generation utilities and learn first to code Swing by hand. You won't regret doing this.
Since you asked, the code I posted here is a Java SSCCE on a different topic. invokeLater is a way of running computations on the EDT. (There is also invokeAndWait, which would work fine here, but under some other conditions can cause a deadlock.)
In fact this example is perhaps a bit over-conservative. Some references say you can run Swing from the main thread the call to show() or setVisible(). However I have a program that misbehaves under Java 7 when I try that.

Main Thread vs. UI Thread in Java

In many Swing snippets given here as answers, there is a call to SwingUtilities#invokeLater from the main method:
public class MyOneClassUiApp {
private constructUi() {
// Some Ui related Code
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyOneClassUiApp().constructUi();
}
});
}
}
But according to the Threads and Swing article, it is safe to construct UI from main thread:
A few methods are thread-safe: In the Swing API documentation,
thread-safe methods are marked with this text:
This method is thread safe, although most Swing methods are not.
An application's GUI can often be constructed and shown in the main
thread: The following typical code is safe, as long as no components
(Swing or otherwise) have been realized:
public class MyApplication {
public static void main(String[] args) {
JFrame f = new JFrame("Labels");
// Add components to
// the frame here...
f.pack();
f.show();
// Don't do any more GUI work here...
}
}
So, is there a real (thread safety) reason to construct the UI in main through SwingUtilities#invokeLater, or this is just a habit, to remember do it in other cases?
"The Swing single-thread rule: Swing components and models should be created, modified, and queried only from the event-dispatching thread."—Java Concurrency in Practice, also discussed here and here. If you don't follow this rule, then you can't reliably construct, modify or query any component or model that may have assumed that you did follow the rule. A program may appear to work correctly, only to fail mysteriously in a different environment. As violations may be obscure, verify correct usage by using one of the approaches mentioned here.
I think that using SwingUtiltities.invokeLater() is just an easier way to execute some code asynchronously. Sometimes it is required for certain application: for example you can create 2 separate windows simultaneously. Nothing more.
It's safe to create your Swing UI in the main method because how would other components be displayed before you set up your UI? As long as you haven't thrown some stuff on the screen already you'll be fine. In other words, this would be bad:
public class MyApplication
{
public static void main(String[] args)
{
JFrame f = new JFrame("Labels");
// Add components to
// the frame here...
f.pack();
f.show();
// now make another frame:
JFrame f2 = new JFrame("Labels2");
// Add components to the 2nd frame here...
f2.pack();
f2.show();
}
}
If you did the above you'd have JFrame f up and running then you'd be adding Swing UI components off the Event Dispatch Thread (EDT). invokeLater runs the code on the EDT - it won't hurt to use it if you want extra peace of mind.

Where is the event dispatch thread called?

I read that all the code which constructs Swing components and handles Events must be run by the Event Dispatch Thread. I understand how this is accomplished by using the SwingUtilities.invokeLater() method. Consider the following code where the GUI initialization is done in the main method itself
public class GridBagLayoutTester extends JPanel implements ActionListener {
public GridBagLayoutTester() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
JButton button = new JButton("Testing");
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1;
button.addActionListener(this);
add(button, gbc);
}
public void actionPerformed(ActionEvent e) {
System.out.println("event handler code");
}
public static void main(String[] args) {
JFrame frame = new JFrame("GridBagLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
frame.setSize(800, 600);
frame.pack();
frame.setVisible(true);
System.out.println("Exiting");
}
}
How is it that this code works perfectly ? We are constructing JFrame and calling a host of other methods in the main thread. I do not understand where exactly the EDT is coming into picture here (what code is it executing ?). The constructor of the GridBagLayoutTester class is also being called from the main method which means the EDT is not running it.
In short
When is the EDT being started ? (does the JVM start the EDT along with the main method if at all the EDT is started while running this code ?)
Does the event handler code for the button run on the EDT ?
The code works perfectly because you are constructing the frame in the main thread, before the EDT has an opportunity to interact with it. Technically, you shouldn't do this ever, but technically you can under this specific circumstance because you cannot interact with the JFrame until it becomes visible.
The main point to know is that Swing components are not thread safe. This means that they cannot be modified from more than one thread at the same time. This is solved by ensuring that all modifications come from the EDT.
The EDT is a thread that's dedicated to user interaction. Any events generated from the user are always run on the EDT. Any user interface updates run on the EDT. For example, when you call Component.repaint(), you can call this from any thread. This simply sets a flag to mark the component as needing a paint, and the EDT does it on its next cycle.
The EDT is started automatically and is tied quite closely into the system implementation. It is handled well within the JVM. Typically, it correlates to a single thread in the windowing system that handles user interaction. Of course, this is quite implementation-dependent. The nice thing is that you don't have to worry about this. You just have to know - if you interact with any Swing components, do it on the EDT.
Likewise, there is one other thing that's important. If you are going to do any long-duration processing or blocking for an external resource, and you are going to do it in response to an event generated by the user, you have to schedule this to run in its own thread off the EDT. If you fail to do this, you will cause the user interface to block while it waits for the long-duration processing to run. Excellent examples are loading from files, reading from a database, or interacting with the network. You can test to see if you are on the EDT (useful for creating neutral methods which can be called from any thread) with the SwingUtilities.isEventDispatchThread() method.
Here are two snippets of code which I use quite frequently when writing Swing programming dealing with the EDT:
void executeOffEDT() {
if (SwingUtilities.isEventDispatchThread()) {
Runnable r = new Runnable() {
#Override
public void run() {
OutsideClass.this.executeOffEDTInternal();
}
};
new Thread(r).start();
} else {
this.executeOffEDTInternal();
}
}
void executeOnEDT() {
if (SwingUtilities.isEventDispatchThread()) {
this.executeOnEDTInternal();
} else {
Runnable r = new Runnable() {
#Override
public void run() {
OutsideClass.this.executeOnEDTInternal();
}
};
SwingUtilities.invokeLater(r);
}
}
The Event Dispatch Thread, as it name implies, is called by Swing every time an event needs to be processed.
In the example you gave, the "Testing" button will automatically call the actionPerformed method when an action event needs to be processed. So, the content of your actionPerformed method will be invoked by the Event Dispatch Thread.
To answer your two final questions:
The EDT is started automatically when the Swing framework loads. You don't have to care about starting this thread, the JRE handles this task for you.
The event handler code is run by the EDT. All events your Swing interface generates are pooled and the EDT is responsible for executing them.
1) I don't know if in new JFrame or in setVisible but it initialize on demand and that's what the end of the main method (over the main process thread) doesn't terminate the process. the EDT was launched and is in a loop blocked waiting the next event.
2) Definitively. That loop receives from the OS the event, find the JButton and tells it that the event was fired. The button then calls the listeners. All that happens in the EDT.
You could review the Swing code that you call when you want to kill the process (or closing the main window) for looking where the EDT is terminated... that can give you a clue (I'll do it later! :)
EDT thread is started after that you firstly call setVisible(true); if it is not started already, ofc. Or if you call SwingUtilities.invokeAndWait() or SwingUtilities.invokeLater() methods.
See http://www.leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html

Categories