I have 3 threads on a frame and when I press the "Exit" button I want o stop the threads which are currently running and after that to close the frame with the program. For that I created an array where I have all the Threads of the frame and when the button "Exit" is pressed the program iterates over the array and if is there any thread running I interrupt it. The problem with my approch is that the program stops only 1 thread, not all of them. So, even though the frame is closed, there will be 2 threads running on the background.
Here is my program:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.Thread.State;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ProgramInterface extends JFrame {
private JButton stopThreadsAndExit;
private CounterThread[] counterThreadsArray = new CounterThread[3];
public static void main(String[] args) {
new ProgramInterface();
}
public ProgramInterface() {
// TODO Auto-generated constructor stub
addCounterThreadsToArray();
addThreadsOnTheFrame();
stopThreadsAndExit = new JButton("Exit");
addActionToExitButton();
add(stopThreadsAndExit);
setFrameSettings();
}
private void setFrameSettings() {
setSize(400, 400);
setLayout(new FlowLayout());
setVisible(true);
}
public void addThreadsOnTheFrame() {
for (CounterThread counter : counterThreadsArray) {
add(counter);
}
}
public void addActionToExitButton() {
stopThreadsAndExit = new JButton("Exit");
stopThreadsAndExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
exit();
}
}).start();
}
});
}
public void exit() {
for (CounterThread counter : counterThreadsArray) {
if (counter.isRunnable()) {
counter.interruptThread();
}
}
dispose();
}
private void addCounterThreadsToArray() {
counterThreadsArray[0] = new CounterThread("Thread 01");
counterThreadsArray[1] = new CounterThread("Thread 02");
counterThreadsArray[2] = new CounterThread("Thread 03");
}
}
class CounterThread extends JPanel {
Thread counter;
private String threadName;
private JButton startThread;
public CounterThread(String threadString) {
this.threadName = threadString;
initializesCounterThread();
addActionToStartButton();
setLayout(new FlowLayout());
add(startThread);
}
public void addActionToStartButton() {
startThread = new JButton("Start " + threadName);
startThread.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
counter.start();
}
});
}
private void initializesCounterThread() {
counter = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
for (int i = 0; i < 1000000000; i++) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
System.out.println(threadName + " generated " + i);
}
} catch (InterruptedException e) {
// TODO: handle exception
Thread.currentThread().interrupt();
}
}
});
}
public void interruptThread() {
counter.interrupt();
}
public boolean isRunnable() {
return counter.getState() == State.RUNNABLE;
}
}
Don't check getState on the thread, you shouldn't have to care if it's currently running or not. Just call interrupt on it.
When you call interrupt on a thread that just sets a flag on that instance. That flag gets checked when the thread does certain things, like wait or sleep, or when the thread explicitly calls isInterrupted (which you are doing). So you don't need to care what state the thread is in.
Related
I want to display a "loading message" when a process is started and I want to change the message when the process is finished. I tried to update the text from a JLabel before and after the thread with the process is started but the problem is that on the frame appears only the last update.
Here is my code:
import java.awt.GridLayout;
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 MyClass extends JFrame {
private JLabel loading;
private JButton jButton;
private JPanel jPanel;
public static void main(String[] args) {
new MyClass();
}
MyClass() {
jPanel = new JPanel();
setLayout(new GridLayout(0, 1));
loading = new JLabel("");
loading.setVisible(true);
jButton = new JButton("Click me!");
addActionToJButon();
setSize(300, 300);
jPanel.add(jButton);
jPanel.add(loading);
add(jPanel);
setVisible(true);
}
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
loading.setText("Loading....");
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
}
}).start();
loading.setText("Done!");
}
});
}
}
I was expecting to appear the label "Loading..." once what the process is started and the message "Done" when the process is finished but I can't find out why on the frame appears the label with the message "Done!".
Thanks to JB Nizet advices I used SwingWorker and the code is working now.
Here is the correct code:
package view;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class MyClass extends JFrame {
private JLabel loading;
private JButton jButton;
private JPanel jPanel;
public static void main(String[] args) {
new MyClass();
}
MyClass() {
jPanel = new JPanel();
setLayout(new GridLayout(0, 1));
loading = new JLabel("");
loading.setVisible(true);
jButton = new JButton("Click me!");
addActionToJButon();
setSize(300, 300);
jPanel.add(jButton);
jPanel.add(loading);
add(jPanel);
setVisible(true);
}
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
loading.setText("Loading....");
swingWorker();
}
});
}
private void swingWorker() {
SwingWorker worker = new SwingWorker<String, String>() {
#Override
protected String doInBackground() throws Exception {
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
return "Done";
}
protected void done() {
try {
String finished = get();
loading.setText(finished);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
worker.execute();
}
}
Because you are doing your "loading" within a thread, and you are setting your loading text outside the thread, you immediately set loading to "Done!" when you begin loading. What you want to do is set loading within your run() function like this:
private void addActionToJButon() {
jButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
new Thread(new Runnable() {
#Override
public void run() {
loading.setText("Loading....");
// TODO Auto-generated method stub
for (int i = 0; i <= 1000000; i++) {
System.out.println(i);
}
loading.setText("Done!");
}
}).start();
}
});
}
I am working on a Uni project to implement a Texas Hold'em poker game. I have been working on a way to to halt the game while waiting for user input (from GUI) if no input is received then the player will be folded.
My initial code just called sleep() on the game thread and then notify() from the controller. The problem I ran into with this method was I had no way to tell if the controller had notified or the that sleep had ended.
To try and flag this I had the controller throw a RuntimeException but have had alot of trouble trying to catch this exception at the game thread.
Here is my minimum viable example:
GUI.java
import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GUI extends JFrame
{
private JButton jb;
private JPanel jp;
private WaitTest wt;
public GUI(WaitTest wt) throws HeadlessException
{
// TODO Auto-generated constructor stub
this.wt = wt;
jp = new JPanel();
jb = new JButton("Throw Runtime");
jp.add(jb);
jb.addActionListener(new TestController(wt));
this.add(jp);
pack();
this.setVisible(true);
}
public GUI(GraphicsConfiguration gc)
{
super(gc);
// TODO Auto-generated constructor stub
}
public GUI(String title) throws HeadlessException
{
super(title);
// TODO Auto-generated constructor stub
}
public GUI(String title, GraphicsConfiguration gc)
{
super(title, gc);
// TODO Auto-generated constructor stub
}
}
TestController.java
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class TestController implements ActionListener
{
private WaitTest wt;
public TestController(WaitTest wt)
{
this.wt = wt;
}
#Override
public void actionPerformed(ActionEvent e){
// TODO Auto-generated method stub
System.out.println("Controller throws runtime");
synchronized(wt.getT())
{
throw new RuntimeException("Runtime flying");
}
}
}
WaitTest.java
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class WaitTest implements Runnable
{
private Thread t;
public WaitTest()
{
// TODO Auto-generated constructor stub
this.t = new Thread();
}
public Thread getT()
{
return t;
}
public void run()
{
try
{
synchronized (this)
{
Thread.sleep(3000);
System.out.println("Player folded");
}
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
System.out.println("Caught ");
}
System.out.println("interupt not received");
}
public void test() throws InterruptedException
{
// TODO Auto-generated method stub
ExecutorService executor = Executors.newSingleThreadExecutor();
synchronized (t)
{
Future<?> future = executor.submit(this);
try
{
future.get();
}
catch (ExecutionException e)
{
System.out.println("hooray!! runtime caught");
t.notify();
return;
}
catch (Exception ex)
{
System.out.println("Runtime caught");
t.notify();
return;
}
}
}
}
Driver.java
public class driver
{
public driver()
{
// TODO Auto-generated constructor stub
}
public static void main(String[] args) throws InterruptedException
{
// TODO Auto-generated method stub
WaitTest wt = new WaitTest();
GUI gui = new GUI(wt);
wt.test();
gui.repaint();
}
}
The runtime Exception is never caught is there a simple way to catch this? or is there a better way to stop the game loop and wait for input? This is my first attempt at thread control/concurrency so I am probably overlooking something simple
I was able to fix this by converting WaitTest to extend TimerTask (which implements Runnable) and changing Thread() to Timer().
Then in the controller instead of needing to throw a Runtime Exception I just called cancel() on the timer
This is much cleaner for my purposes the mucking about with Executors and exceptions
WaitTest.java now looks like this:
import java.util.Timer;
import java.util.TimerTask;
public class WaitTest extends TimerTask
{
private Timer t;
public WaitTest()
{
// TODO Auto-generated constructor stub
this.t = new Timer(true);
}
public void run()
{
System.out.println("Player folded");
}
public void test()
{
// Schedule task for 5 sec time unless the
// controller cancels
t.schedule(this, 5000);
}
public Timer getT()
{
return t;
}
}
public class frame11 extends javax.swing.JFrame implements ActionListener,
PropertyChangeListener {
public String[] columnNames = { "Path",
"File Name",
"Size"};
public Object[][] data ;
int isJPEG (String s) throws IOException
{ int c=0;//not jpeg
if ( (s.endsWith(".JPG")) || (s.endsWith(".JPEG"))||(s.endsWith(".jpeg"))||(s.endsWith(".jpg")))
{
c=1;//is jpeg
}
return c;
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
JFileChooser fch = new JFileChooser("C:\\");
jProgressBar1.setValue(0);
jProgressBar1.setStringPainted(true);
jTextField1.setText(null);
jTextField2.setText(null);
jTextField4.setText(null);
jLabel7.setText(null);
data = new Object[15][3];
jTable2.setModel(new DefaultTableModel(data, columnNames));
fch.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int ret = fch.showOpenDialog(null);
int apr=0;
if (ret==JFileChooser.APPROVE_OPTION)
{ apr=1;
jTextField1.setText(fch.getSelectedFile().toString());
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
}
else jTextField1.setText("Nothing clicked!!!");
if (apr==1) {
jLabel7.setText("Wait Please, While searching ...");
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
EventQueue.invokeLater(new Runnable() { // Added
#Override
public void run() {
File f = fch.getSelectedFile();
String s= f.getAbsolutePath();
int cnt;
int st=0;
Path myfile = Paths.get(s);
if(f.isDirectory()&& Files.isReadable(myfile)){
try {
st=st+CheckFiles(f);
cnt=count(f);
String ss=Integer.toString(cnt);
jTextField2.setText(ss);
jTextField4.setText(Integer.toString(st));
} catch (IOException ex) {
Logger.getLogger(frame1.class.getName()).log(Level.SEVERE, null, ex);
}
}
jLabel7.setText("Scanning Finished. Thanks for waiting ");
}
});
}
}//GEN-LAST:event_jButton1ActionPerformed
private Task task;
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (Integer) evt.getNewValue();
jProgressBar1.setValue(progress);
System.out.println("Property changed");
}
}
//#Override
public void actionPerformed(ActionEvent e) {
}
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
Random random = new Random();
int progress = 0;
setProgress(0);
while (progress < 100) {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException ignore) {}
progress += random.nextInt(10);
setProgress(Math.min(progress, 100));
}
return null;
}
/*
* Executed in event dispatching thread
*/
#Override
public void done() {
Toolkit.getDefaultToolkit().beep();
setCursor(null);
}
}
I would like your help, I'm trying to scan my pc for JPEG images to count them. I have two problems, the first is that I'm using a jtable, but the results is never added until the program ends, and the progress bar isn't synchronized sometimes it ends before the program and sometimes after. please help me resolve these two problems and thank you.
You're using a SwingWorker in order to create a background thread -- good -- but you're making Swing calls directly from that background thread -- bad:
jProgressBar1.setValue(n);
Instead call setProgress(...) from within your SwingWorker, and add a PropertyChangeListener to the worker that listens for changes to the worker's "progress" bound property.
For examples:
How do I make my SwingWorker example work properly?
Cant get JProgressBar to update from SwingWorker class
JProgressBar Tutorial
For an example of an mcve that shows an example of use of a JProgressBar with a SwingWorker:
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class TestProgress2 extends JPanel {
private JProgressBar progressBar = new JProgressBar(0, 100);
private Action startBackgroundTaskAction = new StartBackgroundTaskAction();
public TestProgress2() {
progressBar.setStringPainted(true);
add(progressBar);
add(new JButton(startBackgroundTaskAction));
}
public void setActionEnabled(boolean enabled) {
startBackgroundTaskAction.setEnabled(enabled);
}
private class StartBackgroundTaskAction extends AbstractAction {
public StartBackgroundTaskAction() {
super("Start Background Task");
putValue(MNEMONIC_KEY, KeyEvent.VK_S);
}
#Override
public void actionPerformed(ActionEvent e) {
progressBar.setString(null);
progressBar.setValue(0);
setActionEnabled(false);
MyTask myTask = new MyTask();
myTask.addPropertyChangeListener(new MyTaskListener());
myTask.execute();
}
}
private class MyTaskListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
MyTask myTask = (MyTask) pcEvt.getSource();
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = myTask.getProgress();
progressBar.setValue(progress);
}
if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
setActionEnabled(true);
progressBar.setString("Done");
try {
myTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class MyTask extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
Random random = new Random();
int progress = 0;
setProgress(0);
while (progress < 100) {
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException ignore) {}
progress += random.nextInt(10);
setProgress(Math.min(progress, 100));
}
return null;
}
}
private static void createAndShowGui() {
TestProgress2 mainPanel = new TestProgress2();
JFrame frame = new JFrame("TestProgress2");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have been trying to get this thread to wait, but it doesnt wait or throw an exception or do anything... (I created a new Thread to run the thread because otherwise my gui freezes due to calling the wait method on the edt)
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Sandbox extends JFrame {
boolean paused = false;
Thread thread = new Thread() {
public void run() {
while(true) {
System.out.println("running...");
}
}
};
private JButton button;
public Sandbox() throws Exception {
thread.start();
setSize(300, 150);
setLocationRelativeTo(null);
setDefaultCloseOperation(3);
add(button = new JButton("Pause"));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
synchronized(thread) {
try {
if(button.getText().equals("Pause")) {
thread.wait();
button.setText("Resume");
} else if(button.getText().equals("Resume")) {
thread.notify();
button.setText("Pause");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}.start();
}});
setVisible(true);
}
public static void main(String[] args) throws Exception {
new Sandbox();
}
}
If you are comparing strings you need to use equals() and not ==
if(button.getText().equals("Pause")) {
thread.wait();
button.setText("Resume");
} else if(button.getText().equals("Resume")) {
thread.notify();
button.setText("Pause");
}
But using wait and notify will probably not really do what you want.
i am trying to make a simple reaction test in java.
when the screen turns green i press space witch is supposed to change te boolean "clicked into false and stop the loop that measures time.
in reality the key listner does nothing.
am i adding the keay listener to the right compnent( jpanel panel)?
is there any other problems?
import java.awt.Color;
import java.awt.RenderingHints.Key;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;
public class mainCheck {
// //////////////////////////////////////
public static void timeKeeper() {
boolean clicked=false;
long time = 10000;
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
panel.setBackground(Color.GREEN);
while (time > 0 && !clicked) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
time--;
}
panel.setBackground(Color.gray);
long time2= 10000-time;
JLabel x = new JLabel("" +time2+"");
panel.add(x);
}
// //////////////////////////////////////
static boolean clicked;
JFrame frame;
static JPanel panel;
public mainCheck() {
frame = new JFrame();
panel = new JPanel();
clicked = false;
Handler handler = new Handler();
frame.addKeyListener(handler);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
// //////////////////////////////////////
public static void main(String[] args) {
mainCheck f = new mainCheck();
panel.getActionMap();
f.timeKeeper();
}
// //////////////////////////////////////
public class Handler implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
clicked = false;
System.out.println("space pressed");
}
}
}
}
do not use Thread.sleep(5000); block Event Dispatch Thread and during sleep you can lost all events to the already visible Swing GUI
use Swing Timer instead
Thread.sleep(1); could be proper delay for space enviroment, non_human, very short period attacking latency in Native OS (8-14miliseconds, depends of Native OS)
JLabel x = new JLabel("" +time2+""); and panel.add(x); in AWT/Swing isn't any notifiers that some, any, part of JComponents are removed or added, have to notify used LayoutManager (JPanel has FlowLayout in API) by using methods revalidate and repaint, e.g.
.
JLabel x = new JLabel("" +time2+"");
panel.add(x);
panel.revalidate();
panel.repaint();
Swing GUI should be created on Initial Thread
don't to use KeyListener use KeyBindings instead, otherwise you (are focus hunter) would need to set panel.seFocusable(true);
The problem is that you "e.getKeyCode()" always is "0" so
change for "e.getKeyChar()" and "(char)32"
the other problem is in that you put clicked = false and must be
"true"
And the last problem you have is in "timeKeeper()" you have to erase "boolean" because it is already declarated
Bad
public static void timeKeeper() {
boolean clicked=false
....}
Good
public static void timeKeeper() {
clicked=false
....}
This is the correct code:
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;
public class mainCheck {
// //////////////////////////////////////
public static void timeKeeper() {
clicked=false;
long time = 10000;
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
panel.setBackground(Color.GREEN);
while (time > 0 && !clicked) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
time--;
}
panel.setBackground(Color.gray);
long time2= 10000-time;
JLabel x = new JLabel("" +time2+"");
panel.add(x);
}
// //////////////////////////////////////
static boolean clicked;
JFrame frame;
static JPanel panel;
Handler handler = new Handler();
public mainCheck() {
frame = new JFrame();
panel = new JPanel();
clicked = false;
frame.addKeyListener(handler);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500,500);
frame.setVisible(true);
}
// //////////////////////////////////////
public static void main(String[] args) {
mainCheck f = new mainCheck();
panel.getActionMap();
f.timeKeeper();
}
// //////////////////////////////////////
public class Handler implements KeyListener {
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == (char)32) {
clicked = true;
System.out.println("space pressed");
}
}
}
}
It's a really bad idea to use Thread.sleep() to decrement the time value.
Use a Timer object instead:
public void myTimer(){
Timer myTimer = new Timer(delay, new ActionListener(){
public void actionPerformed(ActionEvent e){
//Stuff to do
}
});
}
Where delay is the amount of time you delay the timer. You start the timer with myTimer.start();