How does Thread.sleep really work? - java

I've made a program that sets a button's setEnable from time to time. The Thread.sleep() is in another class. Here's the code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Try extends JFrame implements ActionListener{
JButton n1 = new JButton("1");
JButton n2 = new JButton("2");
JButton n3 = new JButton("3");
JButton show = new JButton("Show");
{
show.addActionListener(this);
n1.setEnabled(false);
n2.setEnabled(false);
n3.setEnabled(false);
}
public Try(){
super("Try");
setVisible(true);
setSize(500, 200);
setLayout(new GridLayout(1, 4));
add(n1);
add(n2);
add(n3);
add(show);
}
public void actionPerformed(ActionEvent a) {
Object clicked = a.getSource();
if(show == clicked){
new EasyLevel1().start();
}
}
class EasyLevel1 extends Thread {
public void run() {
try {
n1.setEnabled(true);
Thread.sleep(1000);
n1.setEnabled(false);
n2.setEnabled(true);
Thread.sleep(1000);
n2.setEnabled(false);
n3.setEnabled(true);
Thread.sleep(1000);
n3.setEnabled(false);
} catch (InterruptedException e){
}
}
}
public static void main(String[] args){
Try frame = new Try();
frame.setVisible(true);
}
}
However, when I put it on my actionListener within the class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Try extends JFrame implements ActionListener{
JButton n1 = new JButton("1");
JButton n2 = new JButton("2");
JButton n3 = new JButton("3");
JButton show = new JButton("Show");
{
show.addActionListener(this);
n1.setEnabled(false);
n2.setEnabled(false);
n3.setEnabled(false);
}
public Try(){
super("Try");
setVisible(true);
setSize(500, 200);
setLayout(new GridLayout(1, 4));
add(n1);
add(n2);
add(n3);
add(show);
}
public void actionPerformed(ActionEvent a) {
Object clicked = a.getSource();
if(show == clicked){
try {n1.setEnabled(true);
Thread.sleep(1000);
n1.setEnabled(false);
n2.setEnabled(true);
Thread.sleep(1000);
n2.setEnabled(false);
n3.setEnabled(true);
Thread.sleep(1000);
n3.setEnabled(false);
} catch (InterruptedException e){}
}
}
public static void main(String[] args){
Try frame = new Try();
frame.setVisible(true);
}
}
It freezes my whole program, based from that example I've understood that a thread sleep should be ran in another thread to stop the current class from freezing. But I expected the new thread.sleep with still freeze its operations like it will still do the code above but the buttons will be responsive since its in another thread. But surprisingly it did what I wanted it to do, It didn't instantly set everything to disabled like the first program.

Thread.sleep() makes the current thread pause. You are running it in an actionPerformed, that is in a Swing Event. All Swing operations are done in a single thread, the EDT. When you pause it with a Thread.sleep(), Swing can not handle any other event because you haven't returned from the actionPerformed listener. Therefore, the GUI freezes (not the complete application, just the GUI).
In general, it is bad practice to do long-running actions in a Swing event because of this. For what you are trying to do, the good alternative is to use Swing timers.

What is happening is that in the 2nd example Thread.sleep is blocking the EDT so no further UI updates occur. In contrast, in the first example you are calling sleep in a separare Thread so no "freezing" occurs. For tasks like this the use of Swing Timers is preferred.

Thread.sleep will cause the thread that executes the call to sleep for the specified time (or until the thread is interrupted). When you call it in the actionPerformed method, it causes the UI thread to sleep. That's why your program is locking up.
You should start a separate thread that will step through the various calls you want to make while sleeping in between. Alternatively (and much better, in my opinion) you could use Swing timers to do what you want.

Related

is it safe to create a new thread inside actionPerformed in swing?

I created swing application that do some operations in performedAction methode, but when i create a thread that shows the progressBar, the progressBar will not visible during the performedAction methode, but at the end the progressBar will be visible with a value of 100% directly
main class:
import javax.swing.*;
import javax.swing.plaf.ButtonUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class main {
public static void main(String[] args) {
JFrame win = new JFrame("Test");
FlowLayout layout = new FlowLayout();
Button b1 = new Button("Click ");
win.add(b1);
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
window win = new window();
win.start();
try
{
Thread.sleep(2000);
}
catch(InterruptedException e5){}
}
});
win.setLayout(layout);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setVisible(true);
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
window class:
import javax.swing.*;
import java.awt.*;
public class window extends Thread{
public window(){
}
#Override
public void run() {
JFrame fen = new JFrame("New window");
FlowLayout layout = new FlowLayout();
fen.setLayout(layout);
Button b2 = new Button();
fen.setVisible(true);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.add(b2);
try
{
for(int i = 0; i <= 100; i++)
{
b2.setLabel("Button " + i);
Thread.sleep(10);
}
}
catch(InterruptedException e2){
}
}
}
Yes, it is safe to create a new Thread inside of an actionPerformed handler.
But, no thread other than the Event Dispatching Thread (EDT) must interact with the Swing components.
To create animations, or delays inside of Swing, you must use a javax.swing.Timer. Executing a Thread.sleep() on the EDT is never allowed, and won't do want you hope it will.
To return from a spawned thread to the EDT, you must use SwingUtilities.invokeLater() or SwingUtilities.invokeAndWait().
Using a SwingWorker is the preferred way to execute background tasks in Swing. It handles communication and publication of partial and final results from the background task to the EDT for displaying in the GUI components.
The following is a translation of your code to a working example, using a SwingWorker. Instead of AWT Button's, the Swing JButton is used. The program is created using invokeAndWait to ensure the main window construction occurs on the EDT. A lambda function is used, but you can replace this with new Runnable() { } inner class, if desired. The "Button 0" through "Button 100" progress reports are published from the SwingWorker background task, for processing in the EDT. Multiple results can be generated at by the background thread before the EDT has a chance to process them; here, we take only the last result to display in the button.
public class Main {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(() -> {
JFrame win = new JFrame("Test");
win.setLayout(new FlowLayout());
JButton b1 = new JButton("Click");
b1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
DoWork work = new DoWork();
work.execute();
}
});
win.add(b1);
win.setSize(500, 300);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setVisible(true);
});
}
}
class DoWork extends SwingWorker<Void, String> {
JFrame fen;
JButton b2;
DoWork() {
fen = new JFrame("New window");
fen.setLayout(new FlowLayout());
b2 = new JButton();
fen.add(b2);
fen.setSize(100, 100);
fen.setLocationRelativeTo(null);
fen.setVisible(true);
fen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= 100; i++) {
publish("Button " + i);
Thread.sleep(10);
}
return null;
}
#Override
protected void process(List<String> data) {
String last = data.get(data.size() - 1);
b2.setText(last);
}
#Override
protected void done() {
fen.dispose();
}
}
There are still a number of improvements that can be made to this code. A JLabel instead of a JButton for displaying the results, a JDialog for the progress window, or perhaps better a ProgressMonitor. These are left as an exercise to the student.

sleep pauses whole Thread

I know my code isnt like written following the java conventions, but its just a little test...
Ive got a JFrame in my class MainNF. This JFrame should show up and then disappear for a amount of time, which is entered in the JTextField jtf, while another frame, the NFrame frameZero, appears. After that, it should come up again and the frameZero should disappear.
So I just need code that pauses my method for an amount of time.
My Problem: If tried it with Thread.sleep(), but the problem here is the whole Thread then sleeps, so the NFrame frameZero doesnt do anything at this time.
I also tried it with the wait() method, this didnt work either.
My Code:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class MainNF{
public static void main(String[]args){
MainNF m=new MainNF();
}
JTextField jtf;
JFrame j;
JPanel acd;
JPanel [] eingabe;
JButton play;
int letzterScore;
public MainNF(){
j=new JFrame("Menue");
acd=new JPanel();
letzterScore=0;
acd.add(new JLabel("Letzter Score: "+letzterScore));
j.add(acd);
eingabe=new JPanel[2];
eingabe[0]=new JPanel();
eingabe[1]=new JPanel();
eingabe[1].setLayout(new BoxLayout(eingabe[1], BoxLayout.Y_AXIS));
eingabe[1].add(new JLabel("Sekunden fuer das naechste Spiel"));
jtf=new JTextField();
eingabe[1].add(jtf);
eingabe[0].setLayout(new BoxLayout(eingabe[0], BoxLayout.X_AXIS));
eingabe[0].add(eingabe[1]);
play=new JButton("Los!");
play.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){
j.setVisible(false);
letzterScore=play(getPlaytime());
j.setVisible(true);
}});
eingabe[0].add(play);
j.add(eingabe[0]);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
j.setBounds(200, 200, 400, 400);
j.setVisible(true);
}
public int play(){
NFrame frameZero=new NFrame();
try{
Thread.sleep(getPlaytime());
}
catch(InterruptedException e){}
frameZero.setAllInvisible();
return frameZero.amount();
}
public int getPlaytime(){
return Integer.parseInt(jtf.getText());
}
}
Last I tried it with a timer, but that didnt pause anything at all:
public int play(){
NFrame frameZero=new NFrame();
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
}};
Timer t = new Timer(getPlaytime(), taskPerformer);
t.setRepeats(false);
t.start();
frameZero.setAllInvisible();
return frameZero.amount();
}
So I havent any clue what to do...
...would love you if you could help me <3
public int play() is been called from within the context of the Event Dispatching Thread, which is preventing it from, amongst other things, process repaint requests and new events.
Use a Swing Timer instead, see How to use Swing Timers for more details
You might also consider having a look at Concurrency in Swing and The Use of Multiple JFrames, Good/Bad Practice?

Why does the thread not sleep properly?

I am trying to make a simple JButton, which when being clicked causes a simple JLabel to change its text to "second text", after that I want the current thread to sleep for few seconds and finally the JLabel to change its text again, this time to a "third text". I think I have it done here, but it doesn't work the way I want it. The code provided below makes the JButton freeze for the specified timeframe, as if it is held down, and then the label changes to the its third state. Said in other words, the "seconds text" does not appear.
Please advise me how should it be done.
Thank you.
package testPackage;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Demo {
public static void main(String[] args) throws InterruptedException {
JFrame frame = new JFrame();
JButton button = new JButton("Click me!");
final JLabel label = new JLabel("first text");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(600, 600);
JPanel panel = new JPanel();
panel.add(button);
panel.add(label);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText("second text");
try {
Thread.currentThread();
Thread.sleep(4000);
} catch (InterruptedException exc) {
System.out.println("Erorrrrr");
}
}
});
frame.add(panel);
frame.setVisible(true);
}
}
The change of text property is not the only thing that has to be done to see the result when you consider the internals of Java. The control also has to be redrawn (possibly invalidated).
By calling sleep you actually stop java GUI internal worker from redrawing the control you changed. It can happen only after the sleep has finished.
In the action performed method you need to release the main GUI Thread for the changes to occur:
So if you open a new Thread in the actionPerformed method it will release the main GUI thread then after the sleep call the label.setText("third text") this will change the label to second text first wait for 4secs and then change it to third text
#Override
public void actionPerformed(ActionEvent e) {
label.setText("second text");
new Thread(){
public void run(){
try {
//Thread.currentThread();
Thread.sleep(4000);
label.setText("third text");
} catch (InterruptedException exc) {
System.out.println("Erorrrrr");
}
}
}.start();
}
Your GUI runs on a thread. When you sleep that thread for x number of seconds, your GUI freezes for x number of seconds.
As noted in the comments by Marko, "you must instead schedule a delayed event with javax.swing.Timer"
If you sleep on the Event Dispatch Thread (the thread that handles GUI events), then the GUI will freeze. You could start a background thread from the action listener and to the sleeping there.
public void actionPerformed(ActionEvent e)
{
label.setText("text 1");
new Thread(new Runnable()
{
public void run()
{
try {
Thread.sleep(1000);
}catch (InterruptedException ignore){}
// queue Swing code for execution on the EDT
EventQueue.invokeLater(new Runnable()
{
public void run()
{
label.setText("text2");
}
});
}
}).start();
}

How do I create buttons that reset and pause a timer?

How do I make the two buttons that are displayed reset / pause the timer? The timer works but I want to change the code for the buttons so that they will change the timer instead of outputting to the console. Thank you.
CODE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class createWindow extends JFrame implements ActionListener
{
public static void main(String[] args)
{
new createWindow();
}//end main
createWindow()
{
super("Frame");
setSize(400,70);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new FlowLayout());
show();
final JLabel time = new JLabel("Timer");
JButton reset = new JButton("Reset timer");
JButton pause = new JButton("Pause timer");
reset.setActionCommand("resetClicked");
pause.setActionCommand("pauseClicked");
reset.addActionListener(this);
pause.addActionListener(this);
add(pause);
add(time);
add(reset);
long start = System.currentTimeMillis();
while (true)
{
long timer = System.currentTimeMillis() - start;
final int seconds = (int) (timer / 1000);
String display = Integer.toString(seconds);
time.setText(display);
}//end while loop
}//end constructor
#Override
public void actionPerformed(ActionEvent e)
{
String buttonClicked = e.getActionCommand();
if(buttonClicked.equals("resetClicked"))
{
System.out.println("The reset button was clicked"); //Change to reset timer
}
else if(buttonClicked.equals("pauseClicked"))
{
System.out.println("The pause button was clicked"); //Change to pause timer
}
}//end listener
}
Don't use an infinite while loop. This blocks the EDT. Instead use a Swing Timer. This will give you control to start and stop the Timer.
Stopwatch Example
Side Notes:
Don't use JFrame.show as that method is deprecated. Use JFrame.setVisible instead. Also make this call when all components have been added to the frame.
The functionality for the JButtons is sufficiently different to warrant using separate ActionListener instances for each button.
The preferred approach is to use a JFrame instance directly rather then extending it.
Class names in Java begin with uppercase so createWindow would become CreateWindow.

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.

Categories

Resources