Main Thread freezes all other threads inclusive java gui thread - java

NOTE: I work a lot of hours and research google and stackoverflow but I cannot find answer.
I use Thread.sleep() in a JDialog and it freezes all other JDialog, JFrame and threads.
My example code:
public Guitest()
{
setSize(300,300);
// create a JDialog that make guitest wait
MyDialog dlg = new MyDialog();
dlg.setSize(100,100);
dlg.setVisible(true);
while(dlg.isWait())
{
try
{
Thread.sleep(1000);
} catch (InterruptedException ex)
{
Logger.getLogger(Guitest.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("waiting mydialog");
}
}
class MyDialog extends JDialog
{
boolean wait = true;
JButton btn = new JButton("OK");
public MyDialog()
{
setSize(50,50);
btn.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
wait=false;
}
});
add(btn);
}
public boolean isWait()
{
return wait;
}
}
In this situation JDialog does not appear correctly:
inccorect appear jdialog
But it must be apper this:
true appear jdialog
How can I solve this problem. I want to make main thread wait another thread.And someone can correct my sample code or share a sample code with this situation.

IMHO, it appears like you have just one running thread. At first, we draw our JDialog, after that, you sleep your main thread because of the wait flag.
ie. you can't execute your button action listener, thus you can't awake your thread.
Hope it helps understanding.

Thread.Sleep() just sleeps the current thread (i.e. stops it from doing anything, such as redrawing, processing clicks etc), which in your case is the UI thread.
You need to use a worker thread. Any main work that needs to be done that may take a larger amount of time needs to be done in its own thread, and this is the thread that you will want to sleep. It is currently ran alongside the UI components and so this is why you're seeing them freezing.
A good reference is the documentation for concurrency for swing http://docs.oracle.com/javase/tutorial/uiswing/concurrency/
The following may be useful too:
http://java.sun.com/developer/technicalArticles/Threads/swing/
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

Related

Java Swing 2D Game - EventQueue stops firing after CardLayout switch

We want to add a menu to our 2D game.
So far, we had our game loop running on the main thread while the EventQueue from Swing was handling key inputs to our game. Now since we want to add the menu, we no longer can simply start the gameloop from our main method.
The problem: We don't know how to start the gameloop in such a way that the EventQueue keeps receiving the KeyEvents.
Here's what we have in our menu:
startGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)Menu.this.getParent().getLayout();
cl.show(Menu.this.getParent(), "Game");
}
});
So clicking the startGameButton removes the menu from the JFrame and shows the game itself, but the game is frozen, because we don't start the gameloop from our main thread anymore. We think that it's here, that we need to start the gameloop.
So we created a new Thread that will run our gameloop to prevent freezing our UI:
startGameButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CardLayout cl = (CardLayout)Menu.this.getParent().getLayout();
cl.show(Menu.this.getParent(), "Game");
Thread gameThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
game.update();
game.repaint();
Thread.sleep(10);
}
}
});
gameThread.start();
}
});
This works perfectly, the game shows up, and it's running... BUT: Our KeyEvents are no longer firing. Somehow the EventQueue is not picking up keystrokes anymore and we don't know why.
Having the exact same code (creating the Thread and starting it) in the main method without using the CardLayout menu works perfectly fine, also getting KeyEvents. So we think the problem is somewhere in using the CardLayout but we don't know what it is exactly.
Thanks to D-Klotz for the comment about the input maps problem. Even though it was not the correct solution for my problem, it caused me to notice someting:
When switching from the JFrame to another window and then back, the KeyListener was working fine again. So the only line of code missing in the actionPerformed method was:
game.requestFocus();
Works like a charm. Thanks for your help again.

How to implement a delay in ActionListener in Swings?

I have a Swing application where I wish to add some delay. I have a close button, which on clicking should display the JTextArea which displays "Closing database connections...." and then execute Database.databaseClose() method and System.exit(). I have tried using Thread.sleep() method as in the code below for the delay. When I execute the program, the screen freezes for 2 seconds and then closes without displaying the JTextArea. The close button and JTextArea is added to JFrame directly.
What I want is that on clicking the close button, the JTextArea should be displayed immediately and then the application should delay for 2 seconds before finally implementing the Database.databaseClose() method and exiting the program. The Database.databaseClose() method works just fine.
I am a beginner at Swings and would greatly appreciate it if anyone could modify the code to implement the requirement above. Thanks!
Here's the code snippet:
JButton btnClose = new JButton("Close");
btnClose.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JTextArea txtrClosingDatabaseConnections = new JTextArea();
txtrClosingDatabaseConnections.setText("\r\n\tClosing database connections....");
getContentPane().add(txtrClosingDatabaseConnections);
validate();
repaint();
/*
try
{
Thread.sleep(2000);
}
catch (InterruptedException e2)
{
e2.printStackTrace();
}
*/
try
{
Database.databaseClose();
}
catch (Exception e1)
{
e1.printStackTrace();
}
System.exit(0);
}
});
getContentPane().add(btnClose);
Hej, this is an example method that initializes an JMenuBar on a JFrame in Swing.
private JMenuBar initMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
exitApp = new JMenuItem("Exit App");
exitApp.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
JOptionPane.showMessageDialog(getParent(), "Closing App in 2 Seconds");
t.start();
}
});
fileMenu.add(exitApp);
menuBar.add(fileMenu);
return menuBar;
}
May it will help you. It creates an JOptionPane, which must be closed by clicking OK, then the JFrame will be closed after 2 seconds.
Your code is executing on the Event Dispatch Thread, so you can't use a Thread.sleep() since that will block the EDT and prevent it from repainting the GUI.
You need to use a separate Thread for you database processing. Read the section from the Swing tutorial on Concurrency for more information as well as a solution that uses a SwingWorker to manager this Thread for you.
The Timer is the solution. The Swing timer's task is performed in the event dispatch thread. This means that the task can safely manipulate components, but it also means that the task should execute quickly.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
Please go through http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html for more details.

Show an indeterimante progress bar in a JDialog in a thread and run a task in another thread concurrently

When a user clicks a button, a long task of approximately 10 seconds will run. During this time I want to show a progress bar to the user. But the main thread has to wait for the worker thread to finish because the worker thread will set a variable that the main thread will use. If I don't wait the worker thread I will get a NullPointerException when using the variable. So after the worker thread finishes, I will also close the progress bar dialog.
When I wait for the worker thread using join() the progress bar dialog shows (interestingly without the progress bar though) and hangs there.
Thread runnable = new Thread() {
public void run() {
try {
System.out.println("thread basladi");
threadAddSlaveReturnMessage = request.addSlave(
ipField.getText(), passField.getText(),
nicknameField.getText());
System.out.println("thread bitti");
} catch (LMCTagNotFoundException e) {
e.printStackTrace();
}
}
};
Thread runnable_progress = new Thread() {
public void run() {
JTextArea msgLabel;
JDialog dialog;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
dialog = new JDialog(Frame.getFrames()[0], "baslik", true);
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
}
};
runnable.start();
System.out.println("runnable start");
runnable_progress.start();
System.out.println("progress start");
runnable.join();
System.out.println("runnable join");
runnable_progress.join();
System.out.println("progress join");
if (threadAddSlaveReturnMessage.equalsIgnoreCase("OK")) {
fillInventoryTable(inventoryTable);
JOptionPane.showMessageDialog(this, messages.getString("centrum.addslavepanel.SUCCESS"), null, JOptionPane.INFORMATION_MESSAGE);
}
"progress join"
doesn't get printed.
You can use a SwingWorker here. A short example :
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.godel.nio;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.*;
/**
*
* #author internet_2
*/
public class Test {
public static void main(String[] args) {
new Test().doJob();
}
public void doJob() {
JTextArea msgLabel;
JProgressBar progressBar;
final int MAXIMUM = 100;
JPanel panel;
progressBar = new JProgressBar(0, MAXIMUM);
progressBar.setIndeterminate(true);
msgLabel = new JTextArea("deneme");
msgLabel.setEditable(false);
panel = new JPanel(new BorderLayout(5, 5));
panel.add(msgLabel, BorderLayout.PAGE_START);
panel.add(progressBar, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(11, 11, 11, 11));
final JDialog dialog = new JDialog();
dialog.getContentPane().add(panel);
dialog.setResizable(false);
dialog.pack();
dialog.setSize(500, dialog.getHeight());
dialog.setLocationRelativeTo(null);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
msgLabel.setBackground(panel.getBackground());
SwingWorker worker = new SwingWorker() {
#Override
protected void done() {
// Close the dialog
dialog.dispose();
}
#Override
protected void process(List chunks) {
// Here you can process the result of "doInBackGround()"
// Set a variable in the dialog or etc.
}
#Override
protected Object doInBackground() throws Exception {
// Do the long running task here
// Call "publish()" to pass the data to "process()"
// return something meaningful
return null;
}
};
worker.execute();
}
}
Edit : "publish()" should be called in "doInBackground()" to pass the data to "process()".
you have the issue with Concurency is Swing, your GUI is visible after all thread are done
is possible to moving with JProgressBar (I'm talking about you code) but you have to
create and show JDialog, create once time and reuse this container
then to start Thread,
better could be from Runnable#Thread
output to the Swing GUI must be wrapped into invokeLater()
this is exactly job for using SwingWorker and with PropertyChangeListener
As the previous answers already mentioned, SwingWorker is the way to go, if use want to use concurrency with Swing.I found this SwingWorker and ProgressBar tutorial quite useful in understanding how it all works together.
Coming back to your actual problem: I assume you use a GUI, because you stated the user has to click a button. The question is, what is your "main thread" doing? Does it really have to run all the time? Doesn't look like it. Because you said the thread needs a variable which is set by another thread, which is a result of a user interaction. In short: Why does it need to run if it's dependent on a user interaction anyway? The usual way would be to first get all the data you need and then run the calculations or whatever. In your case, either run everything in a single background thread(set the variable first then do the rest), started by the ActionListener of your button or run the other thread after the thread where you set the variable has completed.You could for example use the method done(), provided by SwingWorker, to launch the next task. Or if you really have to, you could wait in a loop for task.isDone()to return true. But don't forget to check for isCancelled() too.Anyway, I think you should rethink your design. Because what I can see from the limited information provided looks overly complicated to me.

Update JTextFields text every 3 seconds after pressing button

So what I want to do is that when I press button JTextField text starts to update to new value every 3 seconds. I have tried Thread sleep metod, but it freezes whole program for the sleep time and after it is over textfields gets the latest input. So here is better explained example of what i am trying to do.
I press the JButton which puts the numbers in JTextFiel every 3 seconds as long as there is available values. I dont want it to append new text, just replace old with new. Anyone got ideas how I can do that? Thanks in advance.
You should use a javax.swing.Timer.
final Timer updater = new Timer(3000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
// update JTextField
}
});
JButton button = new JButton("Click me!");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updater.start();
}
});
The Timer will not block the Event Dispatch Thread (like Thread.sleep does) so it won't cause your program to become unresponsive.
You can't sleep in the EDT. You can either use a swingworker (better solution) or do something like this:
//sleep in new thread
new Thread (new Runnable() {
public void run() {
Thread.sleep(3000);
//update UI in EDT
SwingUtilities.invokelater(new Runnable() {
public void run() {updateYourTextHere();}
});
}
}).start();
You need to get 'the work' done on a separate thread. See some of the answers here: Java GUI Not changing

java SystemTray.add(TrayIcon) hangs Swing GUI on exit

Here's an interesting bug (read: i've probably missed something) in Java Swing I spent the last 2 days trying to trace.
First things first, create a SSCCE.
Here you go.
class GUI extends JFrame{
public static void main(String[] args){
// All GUI work should be handled in the EDT.
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
new GUI().setVisible(true);
}
});
}
GUI(){
// Make a nice icon
ImageIcon img = new ImageIcon(this.getClass().getClassLoader().getResource("img/1.png"));
// Make a TrayIcon with the image
SystemTray sysTray = SystemTray.getSystemTray();
TrayIcon trayIcon = new TrayIcon(img.getImage());
try {
sysTray.add(trayIcon);
}
catch(AWTException e) {
e.printStackTrace();
System.out.println("System Tray unsupported!");
}
this.setTitle("Example GUI");
this.setIconImage(img.getImage());
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
If I run this and close the Window, I would expect it to dispose, and the various Threads to terminate. This is the case if I comment out the "Make a TrayIcon" try/catch block.
The sysTray.add() line seems to not create an Exception, but having it in the code stops the Threads from terminating, as the code hangs on a wait() in the AWT-EventQueue Thread.
Is this a bug, or something I'm missing?
Cheers all.
To get the program to terminate properly when you close it, you need to set the DefaultCloseOperation to EXIT_ON_CLOSE, like this:
GUI.setDefaultCloseOperation(EXIT_ON_CLOSE);
EXIT_ON_CLOSE is defined in JFrame so you don't need to define it or import it from anywhere.
Check the API for more exit operations:

Categories

Resources