How to stop JButton from performing an endless loop - java

Basically what I'm trying to do is continuously append a string of text into a JTextPane when the user clicks a button. The loop will only stop when the user clicks on the button again. This is in my button's actionPerformed method:
StyledDocument xpInfo = txtXPInfo.getStyledDocument();
if (btnGo.getText().equals("Go Adventure!")) {
btnGo.setText("Stop Adventure");
try {
do {
xpInfo.insertString(xpInfo.getLength(), "Some string\n", null);
txtXPInfo.update(txtXPInfo.getGraphics());
Thread.sleep(1000);
} while (btnGo.getText().equals("Stop Adventure"));
} catch (BadLocationException e) {
System.out.println(e);
} catch (InterruptedException ex) {
Logger.getLogger(FrmPlay.class.getName()).log(Level.SEVERE, null, ex);
}
} else if (btnGo.getText().equals("Stop Adventure")) {
btnGo.setText("Go Adventure!");
}
The code I wrote seemed to be an endless loop. I thought it might be because I did all those in the button's actionPerformed method, but I have no idea how else to make it. I'm sorry if this is such a stupid question. I give my thanks in advance to anyone who's willing to answer this question!

You could use ScheduledExecutorService as its main purpose is executing tasks on separate thread with some specified time interval. But you need to keep in mind that all UI-related operations must be done from EDT, so you should wrap txtXPInfo updating operations with SwingUtilities.invokeLater():
private final ScheduledExecutorService xpInfoScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> xpInfoUpdatingFuture;
public void actionPerformed() {
StyledDocument xpInfo = txtXPInfo.getStyledDocument();
if (btnGo.getText().equals("Go Adventure!")) {
btnGo.setText("Stop Adventure");
xpInfoUpdatingFuture = xpInfoScheduledExecutor.scheduleAtFixedRate(
new XpInfoUpdater(), 0, 1, TimeUnit.SECONDS);
} else if (btnGo.getText().equals("Stop Adventure")) {
xpInfoUpdatingFuture.cancel(true);
btnGo.setText("Go Adventure!");
}
}
private class XpInfoUpdater implements Runnable {
#Override
public void run() {
SwingUtilities.invokeLater(() -> {
try {
xpInfo.insertString(xpInfo.getLength(), "Some string\n", null);
txtXPInfo.update(txtXPInfo.getGraphics());
} catch (BadLocationException e) {
System.out.println(e);
}
});
}
}

I think your issue is that you're blocking the Event Thread. In Swing, there's only a single thread that the OS uses to dispatch UI events (like a button press).
In your case, It appears that you're looping infinitely on that thread. If you are, then the other button presses will never register, because that thread is busy with your do/while loop.
What you really want to do is start a different thread (there are lots of examples of this) that do the append loop, and leave the Event Thread for dispatching UI events.

Related

Stop current Thread and then create a new thread that performs the same operation?

My problem is that I cannot figure out a way of having a thread that "on the click of a button starts, and stops on the click of another button", and then if I click the same start button a NEW thread starts that does exactly the same operation as the first. So basically just a new instance.
In my program I have a Server app that has 2 buttons and 2 text fields. After the user has entered the correct username and password the Server app opens a new ServerSocket that listens for clients that want to connect. This is done in a separate Thread to prevent the GUI from freezing. After the stop button is pressed the Thread is stopped.
How can I get my program to start a new Thread, that does the same as the first one, when I press the start button again in the GUI? Must I perhaps make use of a loop?
Server App Code:
public class Server extends JFrame implements ActionListener {
JLabel instructionsLabel;
JLabel passwordLabel;
JPasswordField passwordTF;
JButton shutdownButton;
JButton startupButton;
JLabel usernameLabel;
JTextField usernameTF;
Thread MyThread = new Thread(new ServerRunnable());
public Server() {
super("Server");
initComponents();
}
// My problem is here
public void starterMeth() {
MyThread.start();
}
public void stopMeth() {
MyThread.interrupt();
}
// in these 2 methods
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
String f = "n";
ConnectionBean cb = new ConnectionBean();
char[] a = passwordTF.getPassword();
String b = new String(a);
String inputDetails = usernameTF.getText() + b;
Iterator it = cb.getDetails().iterator();
while (it.hasNext()) {
Object next = it.next();
if (inputDetails.equals(next)) {
f = "y";
if (source == startupButton) {
if (!MyThread.isInterrupted()) {
starterMeth();
JOptionPane.showMessageDialog(null,
"Congratulations! Server started.",
"Start-up Message",
JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null,
"Please restart the server application.",
"Start-up Message",
JOptionPane.INFORMATION_MESSAGE);
}
} else if (source == shutdownButton) {
stopMeth();
JOptionPane.showMessageDialog(null,
"Server shut-down successfully!",
"Shut-down Message",
JOptionPane.INFORMATION_MESSAGE);
}
// only resets the text fields when the correct details are entered
passwordTF.setText("");
usernameTF.setText("");
}
}
if (f.equals("n")) {
JOptionPane.showMessageDialog(null, "Invalid username or password.", "Alert", JOptionPane.WARNING_MESSAGE);
}
cb.setCloseConnection(true);
}
private void initComponents() {
}
}
My Runnable Code:
public class ServerRunnable implements Runnable {
#Override
public void run() {
try {
ServerSocket ss = new ServerSocket(7777);
while(true) {
Socket cs = ss.accept();
new ClientThread(cs).start();
}
} catch (IOException ex) {
}
}
}
Overview
Although the creation of a thread is valid in Java, it is highly discouraged for numerous reasons. The most significant one is that the creation of a thread is quite costly and resource intensive. In addition, there are much safer/efficient models implemented in the standard library that could be used to simplify the issue. In this particular scenario, I would advise against this implementation because of the nature of the operation; start-stop reoccurring. Note, a thread cannot be restarted once it has been started and the only way to stop a thread while executing is to call interrupt(). Unfortunately, interrupting a thread requires the developer to implement error handling in the run() method. Below we will see the run() method of a Runnable or a Thread implementation.
public void run() {
try {
// Your connection logic would be here
yourLogic();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt(); // Maintain status
}
}
Lets assume you made your own thread implementation called MyThreadImpl. Below we will see how to utilize it:
public void starterMeth() {
Thread myThread = new MyThreadImpl(); // Create thread
myThread.start(); // Start execution in parallel
}
public void stopMeth() {
myThread.interrupt(); // Stop the thread
}
Alternatively if you implement your own Runnable like you are in this application, it would look like this:
public void starterMeth() {
Thread myThread = new Thread(new ServerRunnable()); // Create thread
myThread.start(); // Start execution in parallel
}
public void stopMeth() {
myThread.interrupt(); // Stop the thread
}
Although both of theses are valid, there are better approaches.
A better approach
My suggestion is to utilize the CompletableFuture class due to its robust implementation and desirable control. CompletableFutures utilize the global ForkJoinPool.common() for its threading so that the application can execute with more efficiency. In addition, you can receive the Future that is within the object for later use instead of attempting to re-create it each time. Lets investigate how this implementation would work below:
public class Server {
CompletableFuture<Void> myFuture;
...
public void starterMeth() {
myFuture = new CompletableFuture<Void>(); // Create future
myFuture.runAsync(new ServerRunnable()); // Start execution in parallel
}
public void stopMeth() {
myFuture.cancel(true); // Stop the future
}
...
}
Java does not allow to restart a Thread once it has finished executing.
Interrupting the Thread you created will simply finish its execution. The problem here is that you are using the same Thread that has finished executing (once the stop button has been clicked).
I suggest one of the following:
Improve your runnable so that when the user attempts to clicks the shutdownButton button, it stops what it was doing, and acquires some sort of semaphore to make it "sleep" until the startupButton is hit again.
(Easier) Always create a new thread on starterMeth. Don't forget to check if a Thread is running and interrupt it before starting a new thread.
I hope this helps.

Multithreading with one JTextBox

I have some code here that causes an issue for me. I am creating an application that will change the humidity of a greenhouse and then put it in a JTextBox. I have two classes, one to change the humidity and another class that takes care of ambient humidity. The two classes share one text box and the problem is that sometimes two values get printed on one another. How can I do this so that the threads do not overlap with their text box input?
private class humidControl implements Runnable {
public void run() {
try {
screen.setField(String.valueOf(calc.getHumid()), 21);
Thread.sleep(1000*humidTime);
while(on == true) {
calc.changeHumid();
screen.setField(String.valueOf(calc.getHumid()), 21);
if(calc.getHumidifier())
screen.setField("Humidifier is on", 24);
else if (!calc.getHumidifier());
screen.setField("Humidifier is off", 24);
Thread.sleep(1000*humidTime);
}
}
catch (InterruptedException e) {
return;
}
}
}
private class AmbientHumid implements Runnable {
public void run() {
try {
while(on == true) {
calc.ambientHumid();
screen.setField(String.valueOf(calc.getHumid()), 21);
Thread.sleep(1000*AhumidTime);
}
}
catch (InterruptedException e) {
return;
}
}
}
Never ever modify or access any Swing component from any thread other than the event dispatch thread. Swing components are NOT thread safe and are confined to the EDT. Use SwingUtilities.invokeLater() or SwingUtilities.invokeAndWait() from other Threads. SwingWorker also comes in handy when having to do work off the EDT and then modify some Swing component in the end.
Simple, in the humid runnable, you output at 21 and the AmbientHumid outputs at 21 as well, i suggest outputting the AmbientHumid at, say 27, so the overlap won't occur.

Thread Interrupted sometimes works and sometimes not

Please help me with my problem. I have 2 JMenuItems, if I Click on Start it shall start and do stuff. If I click on Stop it shall stop :)
After i clicked on Start, I click on Stop and sometimes it stops and sometimes not. But I want that it always stops.
What have I done wrong? :/
class DiashowListener implements ActionListener {
Thread td;
boolean ok = false;
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Start")) {
td = new Thread(new Runnable() {
public void run() {
if (bimg != null) {
while (!ok) {
try {
...
} catch (Exception e2) {
}
frame.repaint();
}
}
}
});
td.start();
} else if (e.getActionCommand().equals("Stop")) {
if (td != null){
ok = true;
}
}
}
}
EDIT: ok I changed something, its working now, but: If I click on Stop it shall stop immediately.
First of all, you are interrupting the wrong thread (should be td). Second, the contents of the try clause that you omitted is actually important (some operations are uninterruptible). Finally, Thread.isInterrupted is likely not what you want to use, as the flag may get cleared by some unrelated code. Depending on what exactly you are interrupting, it may (or may not) be a good idea to just catch InterruptedException, and terminate in case it is thrown. A better approach is to add your own flag, that the thread will check instead of isInterrupted, and the event handler will set instead of (or in addition to) interrupting the worker thread.

SwingWorker done method throws cancellationexception with get()

I faced an issue of creating stop/start jbuttons for my gui, and after a LOT of googling, i realized i needed multi-threading. Upon further reading i discovered the swingworker class, and i managed to get my GUI to respond to the STOP button.
now my problem is this
The doinbackground() method executes a piece of code that captures packets in an infinite while loop with the condition (!isCancelled), and once it is cancelled (The STOP button executes worker.cancel()) it returns an ArrayList of packets which theoretically, i should be able to obtain inside the done() method using get(). right? But when i try to do this i get a CancellationException and this is driving me nuts right now.
any help would be highly appreaciated!
Thank you
edit: obj is an ArrayList declared outside of the class to store the return values.
here is my code executed by the START jbutton
private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {
final ArrayList packet_list = new ArrayList();
obj.clear();
try {
worker = new SwingWorker<ArrayList,Integer>(){//initialze swingworker class
#Override
protected void done(){
try {
obj = get();
}
catch (InterruptedException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
//opens up stuff required to capture the packets
NetworkInterface [] devices = JpcapCaptor.getDeviceList();
int index = (jComboBox5.getSelectedIndex()-1);
JpcapCaptor captor =JpcapCaptor.openDevice(devices[4], 65535, false, 20);
#Override
protected ArrayList doInBackground(){
while(!isCancelled()){
try {
Packet packets = captor.getPacket(); //captures packets
if (packets != null) //filters out null packets
{
//System.out.println(packets);
packet_list.add(packets); //adds each packet to ArrayList
}
Thread.sleep(100);
} catch (InterruptedException ex) {
return packet_list;
}
}
return packet_list;
}
};
worker.execute();
} catch (IOException ex) {
Logger.getLogger(NewJFrame3.class.getName()).log(Level.SEVERE, null, ex);
}
}
The stop button simply executes
worker.cancel(); no errors there. and this is the swingworker declaration
private SwingWorker<ArrayList,Integer> worker;
cancel doesn't just set the isCancelled flag for you to read at your leisure. That would be pretty much useless. It prevents the task from starting if it hasn't already and may actively interrupt the thread if it's already running. As such, getting a CancellationException is the natural consequence of cancelling a running task.
To further the point, the Javadoc on isCancelled states:
Returns true if this task was cancelled before it completed normally.
Hence if this returns true, then your task cannot complete normally. You cannot cancel a task and expect it to continue as per normal.
SwingWorker docs say "An abstract class to perform lengthy GUI-interaction tasks in a background thread". However, the definition of "lengthly" is different for GUI and for an application lifetime. A 100ms task is very long for a GUI, and is best done by a SwingWorker. A 10 minute task is too long for a SwingWorker simply because it has a limited thread pool, that you may exhaust. Judging by your problem description, you have exactly that - a potentially very long running task. As such, you should rather make a proper background thread than use a SwingWorker.
In that thread, you would have either an AtomicBoolean or simply a volatile boolean flag that you can manually set from the EDT. The thread can then post an event to the EDT with the result.
Code:
class PacketCaptureWorker implements Runnable {
private volatile boolean cancelled = false;
public void cancel() {
cancelled = true;
}
public void run() {
while (!cancelled) {
//do work
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
//Use the result of your computation on the EDT
}
});
}
}
new Thread(new PacketCaptureWorker()).start();
I tried using a volatile boolean instead of using worker.cancel() for the swingworker thread while loop and it works beautifully. (atleast on surface) I managed to create a normal background thread as well and that too worked liked a charm :D Many thanks you saved me a major headache! Wondering what the best method is out of the two.
A follow up, i had to make the volatile boolean available for the whole class, because i had to create 2 seperate instances for the thread class, one to use the START and the other to use the STOP. Apparently two different instances does not address the same instance of the variable. is this bad practice?

creat flashing text by using java multithread method sleep()

hi im creating a flashing text frame by using threading handling method, here is my code:
import javax.swing.*;
public class FlashingText extends JApplet implements Runnable {
/**
*
*/
private static final long serialVersionUID = 1L;
private JLabel jlblText = new JLabel("welcome",JLabel.CENTER);
public FlashingText() {
add(jlblText);
new Thread(this).start();
}
#Override
public void run() {
try {
while(true) {
if(jlblText.getText() == null) {
jlblText.setText("Welcome");
Thread.sleep(2000);
} else
jlblText.setText(null);
}
} catch(InterruptedException ex) {
}
}
}
after i compiled and ran it, it seems the text does not flashing at all
is there anything wrong with my code?
thanks a lot!
There's a better solution, which updates the UI in Event Dispatcher Thread and does not block it.
final JLabel label = new JLabel("Some text");
final Runnable updater = new Runnable() {
#Override
public void run() {
label.setVisible(!label.isVisible());
}
};
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
SwingUtilities.invokeLater(updater);
}
}, 2, 2, TimeUnit.SECONDS);
From the code, it does not really seem that you are flashing anything. Some issues I see with your code:
If the label has no text, the getText() method will yield an empty string ("") and not null.
When updating visual components, you would need to go through the Event Dispatcher Thread (EDT). This is exposed to you through the SwingUtilities.invokeLater(Runnable runnable) class.
It is usually a bad idea to sleep() threads. If you make the changes through the EDT, you would be hanging the ED Thread which will cause the application UI to freeze, which is not desired.
You are swallowing exceptions. In your exception handling, you are not doing anything. It is considered bad practice to not handle exceptions (sometimes a simple log message will do).
According to me there is a problem in the following code block:
try {
while(true) {
if(jlblText.getText() == null) {
jlblText.setText("Welcome");
Thread.sleep(2000);
} else
jlblText.setText(null);
}
}
Because see at the first time the value is welcome, so it will enter the loop and go to else and set it null and then immediately it will check again, as there is no sleep in else so it will check again and enter the if block and set it to welcome, and this whole process will be done at a great speed so you would not be able to see the flashing effect. So I think that you should try putting a sleep at the end of the else block and see, according to me it should work then.
You should change:
else
jlblText.setText(null);
to
else{
jlblText.setText(null);
Thread.sleep(500);
}
or something like this

Categories