How to use JButtons to Start and Pause SwingWorker thread - java

I'm trying to learn Threads in Swing.
I have a Frame with a JProgressBar (progress), five JButtons (Start, Suspend, Resume, Cancel, Close), and a JLabel (label1).
The frame opens. Only Start is enabled. Start calls my class Progressor:
Updated Again Once and For All
Progressor progressor; //declared as class variable, initialized new in constructor and again in overridden done method
Here's the ButtonListener class:
public class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == jbStart) {
progressor.execute();
label1.setText("Progressing ...");
jbCancel.setEnabled(true);
jbResume.setEnabled(true);
jbSuspend.setEnabled(true);
jbClose.setEnabled(true);
}
if(e.getSource() == jbCancel) {
progressor.cancel(true);
label1.setText("Progress Canceled");
}
if (e.getSource() == jbSuspend) {
label1.setText(progressor.suspendProgress());
}
if (e.getSource() == jbResume) {
label1.setText(progressor.resumeProgress());
}
if (e.getSource() == jbClose) {
dispose();
}
}
}//buttonlistener
Here's the SwingWorker class:
public class Progressor extends SwingWorker<Void, Integer> {
private volatile boolean suspend = false;
private Object lock = new Object();
#Override
protected Void doInBackground() {
for (int i = 0; i <= 10; i++) {
checkForSuspend();
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
}
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> list) {
int value = list.get(list.size() - 1);
progress.setValue(value);
}
public void checkForSuspend() {
synchronized (lock) {
while (suspend) {
try {
lock.wait();
} catch (InterruptedException ie){
}
}
}
}//checkForSuspend
#Override
protected void done() {
label1.setText("All Done. Press close to exit");
progressor = new Progressor();
}
public synchronized String suspendProgress() {
suspend = true;
return "Progress suspended ...";
}
public synchronized String resumeProgress() {
synchronized (lock) {
suspend = false;
lock.notify();
return "Progress resumed ...";
}
}
}//Progressor class
Everything works except the cancel doesn't doesn't actually cancel the thread (the progress bar continues).
Should I suspend it before canceling?

This How to Pause and Resume a Thread in Java from another Thread question looks very similar to yours and has some nice examples in the answers.
As for your own code and why it does not work:
You create a new progressor on every click. You should be using and controlling one, instead of creating new ones every time.
When suspending your progressor finishes work instead of suspending. As the above question states - you should be looking at the flag at some points of your computation and acting on it. Example:
while (!cancel) {
if (!suspended) {
for (int i = 1; i <= 10; i++) {
Thread.sleep(1000);
publish(i);
}
}
}
The above code will suspend when it next reaches 10 (unless you resumed it before that), and finish when you press cancel (Cancel needs to be added as an extra flag in the obvious manner).

Your thread should run inside a while loop that looks for a boolean to change value from another object, then simply change the state with setPause(true/false) when you click the button:
while(true){
if(object_everyone_can_reference.getPause()){
Thread.sleep(1000);
}
}

Related

Java - ExecutorService Cancelling Overhead

I am writing a program that generates images in response to user interaction (panning). For each image I create a task and submit this to a ThreadPool. To make the panning more responsive I create two versions of the image needed, one low resolution one and then a high resolution one. The high resolution image can take more than a second to calculate and if the user is panning a lot I do not want to bog down the processor with calculating old high res images. To manage this I have created an UpdateHandler (this is an inner class) in my program that is supposed to sort this out.
On occation some processes that should have been cancelled slip through. What am I doing wrong, why is this not thread safe? I am not great at multi-threading so any tips are welcome.
Updatehandler:
private class UpdateHandler {
private boolean cancelled = false;
private int number;
private Future<WritableImage> future;
private ChangeListener<Number> listener = new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if(newValue.intValue() > number) {
cancel();
}
}
};
private UpdateHandler() {
number = numberOfUpdaters.get() + 1;
numberOfUpdaters.set(number);
numberOfUpdaters.addListener(listener);
if(number < numberOfUpdaters.get()) {//If the number was increased before the listener was applied.
cancel();
}
updateLowResImage();
int resolution = (int) rightPane.resolutionField.getValue();
if(resolution > lowResolution && !isCancelled()) {
new Thread(new Runnable() {
#Override
public void run() {
EnhancedCallable<WritableImage> task = getImageTask(resolution, resolution);
task.setDescription("Generating " + name);
future = threadPool.submit(task);
if(isCancelled()) {//If cancelled before construction but after check cancel again.
cancel();
}
try {
WritableImage image = future.get();
if(image != null) {
setImage(image);
}
} catch (ExecutionException | InterruptedException | CancellationException e) {
//Do nothing.
} finally {//When we are done cancel to make sure the listener is removed.
cancel();
}
}
}).start();
}
}
/**
* Cancels the future or prevents it from starting. Can be called several times without problems.
*/
private void cancel() {
cancelled = true;
try {
if(!future.isDone() && !future.isCancelled()) {//If Future is still active.
future.cancel(true);
}
if(future.isDone() || future.isCancelled()) {//If Future is not active anymore.
numberOfUpdaters.removeListener(listener);
}
} catch(NullPointerException e) {//If the Future is not yet initialised.
numberOfUpdaters.removeListener(listener);
}
}
private boolean isCancelled() {
return cancelled;
}
}

How can i make System.in read from a JTextField

I am trying to make System.in InputStream read from a JTextField.
What i need it to do, is to allow .read() to proceed once a user presses enter in a JTextField with a String in it.
The issue is that I don't know when .read() will be invoked, and i want it to block without freezing the main thread, until enter is pressed by the user in the JTextField where it will notify the thread waiting.
So far i tried the following:
public class InputStreamHandlerThread extends Thread {
private JTextField txt;
public InputStreamHandlerThread(JTextField txt) {
this.txt = txt;
start();
}
#Override
public void run() {
System.setIn(new FakeInputStream());
}
class FakeInputStream extends InputStream {
private int indx = 0;
#Override
public int read() throws IOException {
if (indx == txt.getText().length()) {
indx = 0;
try {
synchronized (this) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int byt = txt.getText().getBytes()[indx];
indx++;
return byt;
}
}
}
which is initialized and started by the GUI thread.
So once the GUI loads, it creates an instance of this class and keeps a pointer so that when the enter key is pressed on the keyboard within the JTextField, it is notified to read from it.
in = new JTextField();
in.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent a) {
if (a.getKeyChar() == '\n') {
inputStreamHandler.notify();
}
}
});
So currently there are three threads:
1. The main thread on what the GUI runs on
2. The InputStream handler thread (See above ^)
3. The thread that reads from System.in
The problem is that once i invoke inputStreamHandler.notify(); it throws a java.lang.IllegalMonitorStateException which according to the docs, is thrown if the thread is not the holder of the lock.
How do i resolve this?
Thank you :-)
String text;
...
in.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
text = in.getText();
}
});
Make sure you make text and in both fields.

Background Thread fails while setting EditText's value

I have a thread that call's MainActivies method that sets text value of EditText but it fails.
MyRunnable is like that:
public class MyRunnable implements Runnable {
MainActivity mController;
public SoundTrigger(MainActivity pController) {
mController = pController;
}
#Override
public void run() {
for (int i = 0; i < 10000; i++) {
if(i % 100) {
mController.triggered(i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
In MainActivity class my triggered method like this:
public void triggered(int nNum) {
EditTextInstance.setText(nNum+"");
}
but it fails. All i need is printing a real time data that is created by a background thread on an edittext component.
make it like this:
#Override
public void run() {
for (int i = 0; i < 10000; i++) {
if(i % 100) {
mController.runOnUiThread(new Runnable() {
public void run() {
mController.triggered(i);
}
});
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Explanation: you were previously changing an UI element from a Non-UI thread and this is not good. This code should work, BUT it is still not the recommended way to approach the problem. You should use something like a AsyncTask where you have a template method for 'background' operation (non-ui thread operation) and the 'onPostExecute' which already executes on the UI thread.
another additional note: NEVER call Thread.sleep(); on the UI Thread (a.k.a. Main thread)

Pausing SwingWorker

Im a beginner to GUI and multithreading. I currently have a simulation which runs through a bug moving about in the console. I want to be able to pause the bug using a button. I have two buttons (run and pause) the run button will start the simulation and the pause button should pause it ( or make it sleep for a bit) ive managed to get the run button working but i am then unable to click pause once its running (because its in the same thread i believe) Ive read into it alot but still cant seem to work it out.. any help would be massively appreciated..
//IN MY ACTION LISTENER..
else if (E.getSource() == Pause) {
Worker pauseWorker = new Worker();
pauseWorker.execute();
IN MY NEW WORKER CLASS
import javax.swing.SwingWorker;
public class Worker extends SwingWorker<Void, Void> {
#Override
protected Void doInBackground() throws Exception {
// System.out.println("Background");
for (int i = 0; i <= 1; i++) {
Thread.sleep(1000);
System.out.println("Background running");
}
return null;
}
}
else if (E.getSource() == Pause) {
Worker pauseWorker = new Worker();
pauseWorker.execute();
This starts a new worker, does not stop the running one.
Instead, you can keep a reference to the background worker and cancel() it when the pause button is pressed. See SwingWorker.cancel()
else if (E.getSource() == Pause) {
worker.cancel(true);
}
And in the worker class, regularly check if you've been cancelled:
#Override
protected Void doInBackground() throws Exception {
// System.out.println("Background");
while(!isCancelled()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("Background interrupted");
}
System.out.println("Background running");
}
return null;
}
If you really do need to pause rather than cancel the worker, you'll have to write your own pause() method and do the administration yourself.
To give you some idea, something like this goes into the worker class then:
boolean paused = false;
public void pause() {
paused = true;
}
public synchronized void resume() {
paused = false;
this.notify();
}
#Override
protected Void doInBackground() throws Exception {
while(!isCancelled()) {
if( paused ) {
System.out.println("Background paused, waiting for resume");
try {
synchronized(this){
wait(1000);
}
} catch (InterruptedException ex) {
System.out.println("Background interrupted");
}
} else {
System.out.println("Background running");
// do a chunk of actual work
}
}
return null;
}

How to make a checkbox do a continuous task, until it gets unchecked?

Anyone have any idea how to make check box do a task until it gets unchecked?
JCheckBox chckbxInspecAuto = new JCheckBox("Inspe. Auto.");
chckbxInspecAuto.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0)
{
try {
gimage = vision.grab();
cvSaveImage("image001.bmp", gimage);
ipanel.loadImage("image001.bmp");
} catch (com.googlecode.javacv.FrameGrabber.Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
EDIT: The code is over there now...
You could extend the SwingWorker class or even Thread to implement the functionality you want the checkbox to control. Create methods contiueWorking() and stopWorking() and in the run method check some boolean flag to see whether to do the task or sleep.
As Robin commented on a post below, swing components should only be accessed from the Event Worker Thread. I violated this rule in my example below because I wanted to produce something simple and since the running thread spends most of its time sleeping, it works fine.
I would use SwingWorker for a more time consuming task that I wish to update the user on as it executes. For instance, say we have a thread generate and then send a set of email messages, and for each e-mail, display some text in the UI that confirms the message was sent or indicates why it was undeliverable. Each message may lock the Thread up for a while. The UI will not be updated in sync with the Thread's execution of the e-mail task. SwingWorker to the rescue. The java2s.com site has a SwingWokerDemo.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
/**
* The sequence of prime integers is calculated and displayed on the screen.
* This little program demonstrates how a checkbox can be used
* to start and stop a thread.
*/
public class ThreadDemo extends JFrame {
JCheckBox runningCB;
JTextArea displayArea;
JTextField delayField;
PrimeCalcThread workerThread = null;
public ThreadDemo() {
super("Prime Numbers");
runningCB = new JCheckBox("Calculate Primes");
JPanel topPanel = new JPanel();
topPanel.add(runningCB);
topPanel.add(new JLabel("Dealy: "));
delayField = new JTextField(10);
delayField.setText("500");
topPanel.add(delayField);
getContentPane().add(topPanel,BorderLayout.NORTH);
displayArea = new JTextArea(30,80);
displayArea.setText("2, 3, 5, ");
displayArea.setLineWrap(true);
JScrollPane scroller = new JScrollPane(displayArea);
getContentPane().add(scroller,BorderLayout.CENTER);
pack();
runningCB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(runningCB.isSelected() ) {
if(workerThread != null)
workerThread.contiueWorking();
else {
workerThread = new PrimeCalcThread();
workerThread.start();
}
}
else {
if(workerThread != null)
workerThread.stopWorking();
}
}
});
delayField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String input = delayField.getText().trim();
try {
int d = Integer.parseInt(input);
workerThread.setDelay(d);
}
catch(NumberFormatException nfe) { }
}
});
setVisible(true);
}
public static void main(String[] arg) {
ThreadDemo window = new ThreadDemo();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* When this thread is active it calculates prime numbers. The Thread
* continues until it is paused with stopWorking() or until the
* boolean field endThread is set to true.
*/
class PrimeCalcThread extends Thread {
ArrayList<Integer> primeList;
int delay = 500;
boolean active = true;
boolean endThread = false;
private int lastPrime;
public PrimeCalcThread() {
primeList = new ArrayList<> (1024);
primeList.add(2); primeList.add(3); primeList.add(5);
lastPrime=5;
}
public void run() {
while(!endThread) {
if(active) {
calcNextPrime();
displayArea.append(lastPrime + ", ");
}
try { sleep(delay); }
catch(InterruptedException whatever) { }
}
}
private void calcNextPrime() {
int p = lastPrime+2;
while(!isPrime(p))
p+=2;
primeList.add(p);
lastPrime = p;
}
/**
* Checks if p is prime by checking for divisibility by all primes in the
* calculated primeList so far. This method only works if sqrt(p) < lastPrime
*/
private boolean isPrime(int p) {
int maxCheck = (int) Math.sqrt(p) + 1;
for(int prime: primeList) {
if(p % prime == 0)
return false;
if(prime > maxCheck)
break;
}
return true;
}
public int getLastPrime() {
return lastPrime;
}
public ArrayList<Integer> getPrimeList() {
return primeList;
}
public int getDelay() {
return delay;
}
public void setDelay(int waitTime) {
if(waitTime>=0)
delay = waitTime;
}
public void contiueWorking() {
active=true;
}
public void stopWorking() {
active=false;
}
}
}
- Well create a Daemon Thread and run it in an infinite loop, let it keep checking whether the JCheckBox is checked or not using isSelected() method or ItemListener.
- And when the JCheckBox gets selected then create a Thread to execute the task, or use SwingWorker Class which properly synchronizes the UI and Non-UI thread's work.
It depends on the task.
If a task is on other process on other machine you could simply send the right messages.
If the task is a separated thread in the same application you could do what wxyz suggests or you could use some kind of Listener with the ability of start and stop the Thread (the thread is always the same or you are creating a new one every time you select the checkbox?).
My favorite one would be to use Observer pattern in the "PUSH" way, so you would use some kind of external signal to the thread which when sent would create and/or stop it.

Categories

Resources