Cancel long task in javax.swing framework - java

I have a GUI with java.swing components, ActionListeners, and a SwingWorker to execute further code in a separate thread. I understand that a SwingWorker can only be created once and can't be terminated, but cancelled. Further I believe it to be good practice to check the SwingWorker status with its method isCancelled() and in case to exit the doInBackground() method and react in the done() method accordingly. This works fine if you have for example a loop within the doInBackground() method and can test isCancelled() at every iteration.
But how can you really break/terminate a long task that is executed within the doInBackground() method, such as reading a large csv (>1GB) or calling a process intensive method from another class? To illustrate my question I constructed a simple program that shows my problem when you choose a large input csv. The stop button works fine with the counter loop but doesn't terminate the csv import.
How can I actually break/terminate a long lasting process? If this isn't possible with SwingWorker, how would I do that with threads? Would thread.interrupt() be a possibility? How would I implement that in my example?
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import com.opencsv.CSVReader;
public class MinimalSwing extends JFrame {
// fields
private JButton fileButton, startButton, stopButton;
private JLabel displayLabel;
private File csvIn;
private SwingWorkerClass swingWorker;
// constructor
public MinimalSwing() {
// set GUI-window properties
setSize(300, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocation(200, 200);
setTitle("MinimalSwing");
setLayout(new BorderLayout(9, 9));
setResizable(false);
// set components
fileButton = new JButton("Choose File");
fileButton.addActionListener(new ButtonActionListener());
getContentPane().add("North", fileButton);
startButton = new JButton("Start");
startButton.setEnabled(false);
startButton.addActionListener(new ButtonActionListener());
getContentPane().add("West", startButton);
stopButton = new JButton("Stop");
stopButton.setEnabled(false);
stopButton.addActionListener(new ButtonActionListener());
getContentPane().add("East", stopButton);
displayLabel = new JLabel("Status...");
getContentPane().add("South", displayLabel);
}
// csvFileChooser for import
private File getCsv() {
JFileChooser fc = new JFileChooser();
int openDialogReturnVal = fc.showOpenDialog(null);
if(openDialogReturnVal != JFileChooser.APPROVE_OPTION){
System.out.println("ERROR: Invalid file choice.");
}
return fc.getSelectedFile();
}
// csvImporter
private class CsvImporter {
public void readCsv(File file) throws IOException {
CSVReader reader = new CSVReader(new FileReader(file));
String [] nextLine;
reader.readNext();
while ((nextLine = reader.readNext()) != null) {
displayLabel.setText("..still reading");
}
reader.close();
displayLabel.setText("..actually done.");
}
}
// ActionListener
private class ButtonActionListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == fileButton) {
csvIn = getCsv();
if(csvIn != null) {
startButton.setEnabled(true);
stopButton.setEnabled(true);
}
}
else if(e.getSource() == startButton) {
fileButton.setEnabled(false);
startButton.setEnabled(false);
stopButton.setEnabled(true);
swingWorker = new SwingWorkerClass();
swingWorker.execute();
}
else {
fileButton.setEnabled(true);
startButton.setEnabled(true);
stopButton.setEnabled(false);
swingWorker.cancel(true);
}
}
}
// swingWorker to interact with further program
private class SwingWorkerClass extends SwingWorker<Boolean, Void> {
#Override
protected Boolean doInBackground() throws Exception {
long t0 = System.currentTimeMillis();
displayLabel.setText("starting execution...");
displayLabel.setText("..importing csv");
CsvImporter csvImporter = new CsvImporter();
csvImporter.readCsv(csvIn);
if(isCancelled()) return false; // this cancels after the import, but I want to cancel during the import...
long t1 = System.currentTimeMillis();
displayLabel.setText("csv imported in " + String.format("%,d", t1 - t0) + " ms");
for(short i=1; i<=10; i++) {
if(isCancelled()) return false; // this works fine as it is called every second
TimeUnit.SECONDS.sleep(1);
displayLabel.setText("counter: " + i);
}
return true;
}
#Override
public void done() {
fileButton.setEnabled(true);
startButton.setEnabled(true);
stopButton.setEnabled(false);
if(isCancelled()) {
displayLabel.setText("Execution cancelled.");
}
else {
displayLabel.setText("Execution succeeded.");
}
}
}
// main
public static void main(String[] args) throws URISyntaxException, IOException {
// launch gui
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
MinimalSwing frame = new MinimalSwing();
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
}

You can make your CSVImporter extend SwingWorker instead of having one more class SwingWorkerClass. In that way you can get more control and cancel the import task.
Something like below.
private class CsvImporter extends SwingWorker<Boolean, Void> {
public boolean readCsv(File file) throws IOException {
CSVReader reader = new CSVReader(new FileReader(file));
String[] nextLine;
reader.readNext();
while ((nextLine = reader.readNext()) != null) {
displayLabel.setText("..still reading");
if (isCancelled())
return false; // this cancels after the import, but I want
// to cancel during the import...
}
reader.close();
displayLabel.setText("..actually done.");
return true; // read complete
}
#Override
protected Boolean doInBackground() throws Exception {
long t0 = System.currentTimeMillis();
displayLabel.setText("starting execution...");
displayLabel.setText("..importing csv");
CsvImporter csvImporter = new CsvImporter();
boolean readStatus = csvImporter.readCsv(csvIn);
if (readStatus) {
long t1 = System.currentTimeMillis();
displayLabel.setText("csv imported in " + String.format("%,d", t1 - t0) + " ms");
for (short i = 1; i <= 10; i++) {
if (isCancelled())
return false; // this works fine as it is called every second
TimeUnit.SECONDS.sleep(1);
displayLabel.setText("counter: " + i);
}
}
return readStatus;
}
}

Related

Multi-threading with GUI in Java

I'm working on a GUI for a program that is computationally intensive and takes some period of time to complete calculations. I want to display and update the processing time on the GUI, both for reference and as an indication to the user that the program is running. I've created a worker to deal with the processing time on a separate thread as follows:
public class Worker extends SwingWorker<String, String>{
JLabel label;
boolean run;
public Worker(JLabel label)
{
this.label = label;
this.run = true;
}
#Override
protected String doInBackground() throws Exception {
//This is what's called in the .execute method
long startTime = System.nanoTime();
while(run)
{
//This sends the results to the .process method
publish(String.valueOf(System.nanoTime() - startTime));
Thread.sleep(100);
}
return null;
}
public void stop()
{
run = false;
}
#Override
protected void process(List<String> item) {
double seconds = Long.parseLong(item.get(item.size()-1))/1000000000.0;
String secs = String.format("%.2f", seconds);
//This updates the UI
label.setText("Processing Time: " + secs + " secs");
label.repaint();
}
}
I pass a JLabel to the Worker which it displays the processing time on. The following code creates the Worker and executes a runnable that carries out the main calculations.
Worker worker = new Worker(jLabelProcessTime);
worker.execute();
//Check for results truncation
boolean truncate = !jCheckBoxTruncate.isSelected();
long startTime = System.nanoTime();
String[] args = {fileName};
//run solution and draw graph
SpeciesSelection specSel = new SpeciesSelection(args, truncate);
Thread t = new Thread(specSel);
t.start();
t.join();
ArrayList<Double> result = specSel.getResult();
drawGraph(result);
worker.stop();
My problem is that the processing time does not update on the GUI until after the calculations have finished. I think I'm pretty close because without 't.join();' the timer updates fine, but the processing never completes. I'd really appreciate some help to figure out what's wrong.
Your code is not working as you think it is...
I created MVCE for you...
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
public class SwingWorkerTest extends JFrame {
public SwingWorkerTest() {
this.setLayout(new FlowLayout());
JButton button = new JButton("run");
JLabel label = new JLabel("time: -");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Worker worker = new Worker(label);
worker.execute();
//Check for results truncation
// boolean truncate = !jCheckBoxTruncate.isSelected();
// long startTime = System.nanoTime();
// String[] args = {fileName};
//run solution and draw graph
// SpeciesSelection specSel = new SpeciesSelection(args, truncate);
// Thread t = new Thread(specSel);
// t.start();
// t.join();
// ArrayList<Double> result = specSel.getResult();
// drawGraph(result);
worker.stop();
System.out.println("button's actionPerformed finished");
}
});
this.getContentPane().add(button);
this.getContentPane().add(label);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args) {
new SwingWorkerTest();
}
}
class Worker extends SwingWorker<String, String>{
JLabel label;
boolean run;
public Worker(JLabel label)
{
this.label = label;
this.run = true;
}
#Override
protected String doInBackground() throws Exception {
System.out.println("doInBackground..., run=" + run);
//This is what's called in the .execute method
long startTime = System.nanoTime();
// while(run)
// {
System.out.println("running...");
//This sends the results to the .process method
publish(String.valueOf(System.nanoTime() - startTime));
Thread.sleep(100);
// }
System.out.println("worker finished...");
return null;
}
public void stop()
{
// System.out.println("stop");
// run = false;
}
#Override
protected void process(List<String> item) {
System.out.println("processed");
double seconds = Long.parseLong(item.get(item.size()-1))/1000000000.0;
String secs = String.format("%.2f", seconds);
//This updates the UI
System.out.println("updating");
label.setText("Processing Time: " + secs + " secs");
// label.repaint();
}
}
In short I found, that Worker.stop() is called before doInBackground as a result, your run is false and so publish is never called.
The "fixed" code above prints (after start I resized and I clicked on run button):
button's actionPerformed finished
doInBackground..., run=true
running...
processed
updating
worker finished...
and it shows:
new approach with a timer
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.SwingWorker;
import javax.swing.SwingWorker.StateValue;
import javax.swing.Timer;
public class SwingWorkerTestNew extends JFrame {
int progress = 0;
public SwingWorkerTestNew() {
GridLayout layout = new GridLayout(2, 1);
JButton button = new JButton("run");
JLabel label = new JLabel("progress: -");
WorkerNew worker = new WorkerNew(label);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
worker.execute();
System.out.println("button's actionPerformed finished");
}
});
this.getContentPane().setLayout(layout);
this.getContentPane().add(button);
this.getContentPane().add(label);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
Timer timer = new Timer(100, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (worker.getState() == StateValue.STARTED) {
++progress;
label.setText(Integer.toString(progress));
}
if (worker.getState() == StateValue.DONE) {
label.setText("done");
}
}
});
timer.start();
}
public static void main(String[] args) {
new SwingWorkerTestNew();
}
}
class WorkerNew extends SwingWorker<String, String> {
JLabel label;
public WorkerNew(JLabel label) {
this.label = label;
}
#Override
protected String doInBackground() throws Exception {
System.out.println("background");
Thread.sleep(2000);
System.out.println("done");
return null;
}
}
I was going about this in a far too complicated manner. No SwingWorker was required. I solved it as follows:
//Check for results truncation
boolean truncate = !jCheckBoxTruncate.isSelected();
String[] args = {fileName};
//run solution and draw graph
SpeciesSelection specSel = new SpeciesSelection(args, truncate);
Thread t = new Thread(specSel);
t.start();
long startTime = System.nanoTime();
new Thread()
{
public void run() {
while(!specSel.isFinished())
{
double seconds = (System.nanoTime() - startTime)/1000000000.0;
String secs = String.format("%.2f", seconds);
jLabelProcessTime.setText("Processing Time: " + secs + " secs");
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(SpecSelGUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
ArrayList<Double> result = specSel.getResult();
drawGraph(result);
}
}.start();

ScheduledThreadPoolExecutor scheduled purging memory leak

I am trying to write a enclosed class to download images from the web that will refresh every 30 seconds. I might want to download 1 image or I might want to download N images. And I might want to stop downloading a certain image at any time. I wrote the following class that works great except when I stop downloading an image memory is not being released for that task. Or if I stop all images from being downloaded memory is not released (This won't happen in production). I have tried several different ways to achieve this. My last attempt was to purge the tasks from the ScheduledThreadPoolExecutor every 30 seconds using the same executor or with the code below a separate executor. I am also providing code to test releasing memory, although my example stops all images from being downloaded when in a real life usage I should be able only stop one image and the memory be released from that one task.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
public class ImageLoadTask implements Runnable {
private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
private static Runnable purgeRunnable = () -> purge();
private ScheduledFuture<?> scheduledFuture;
private URL pictureURL;
private Consumer<BufferedImage> successMethod;
private Consumer<String> failMethod;
private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
private boolean displaySuccess = false;
private boolean displayError = false;
private boolean isCancelled = false;
static {
taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskExecutorService.setRemoveOnCancelPolicy(true);
purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
}
public ImageLoadTask(String url) {
try {
this.pictureURL = new URL(url);
} catch (MalformedURLException e) {
if(failMethod != null) {
failMethod.accept(e.getMessage()); ;
}
if(displayError) {
System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
}
}
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
this(url);
this.successMethod = successMethod;
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
this(url, successMethod);
this.failMethod = failMethod;
}
public void start() {
scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
}
public void stop() {
if(isCancelled)
return;
isCancelled = true;
scheduledFuture.cancel(true);
scheduledFuture = null;
pictureURL = null;
successMethod = null;
failMethod = null;
streamHandler = null;
taskExecutorService.remove(this);
taskExecutorService.purge();
}
public static void purge() {
System.out.println("Purging");
taskExecutorService.purge();
}
#Override
public void run() {
if(!isCancelled) {
try {
BufferedImage image = loadImage(pictureURL);
if(displaySuccess) {
System.out.println("Image received for url " + pictureURL);
}
if(successMethod != null && !isCancelled) {
successMethod.accept(image); ;
}
} catch (IOException e) {
if(failMethod != null && !isCancelled) {
failMethod.accept(e.getMessage());
}
if(displayError) {
System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
}
}
}
}
public void displayError(boolean displayError) {
this.displayError = displayError;
}
public void displaySuccess(boolean displaySuccess) {
this.displaySuccess = displaySuccess;
}
private BufferedImage loadImage(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = streamHandler.openConnection(input).getInputStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = ImageIO.createImageInputStream(istream);
BufferedImage bi;
try {
bi = ImageIO.read(stream);
if (bi == null) {
stream.close();
}
} finally {
istream.close();
}
return bi;
}
#Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
class ImageURLStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL url) throws IOException {
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(60000); // 1 min
connection.setReadTimeout(60000); // 1 min
return connection;
}
}
}
Test App:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLoadTaskTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Gui gui = new Gui();
}
});
}
static class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private List<ImageLoadTask> tasks = new ArrayList<>();
private boolean running = false;
private JButton startStopButton = new JButton("Start");
private JButton purgeButton = new JButton("Purge");
private ActionListener startStopListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(running) {
stopTasks();
} else {
startTasks();
}
}
};
private ActionListener purgeListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ImageLoadTask.purge();
}
};
public Gui() {
setTitle("Image Load Task Test");
setBounds(250, 250, 300, 150); // Size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
startStopButton.addActionListener(startStopListener);
contentPanel.add(startStopButton);
purgeButton.addActionListener(purgeListener);
contentPanel.add(purgeButton);
setVisible(true);
}
private void startTasks() {
running = true;
System.out.println("Starting tasks");
for(int i = 0; i < 2500; i++) {
ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
task.start();
tasks.add(task);
}
startStopButton.setText("Stop");
}
private void stopTasks() {
running = false;
System.out.println("Stopping " + tasks.size() + " tasks");
for(ImageLoadTask task : tasks) {
task.stop();
}
tasks.clear();
startStopButton.setText("Start");
System.out.println("Stopped tasks ");
//ImageLoadTask.purge();
}
private void success(BufferedImage image) {
//System.out.println("Success!");
}
private void fail(String message) {
//System.out.println("Fail! "+ message);
}
}
}
You don't close your 'stream' when you interrupt ImageIO.read(stream).

JFrame appears blank after the first time calling it

This JFrame the first time I call it when the application is running, works perfectly. But when I try to open a new file, the progress bar is meant to return to the screen. See the screen grabs to see how it should look at how it looks thereafter.
public ArrayList<Data> handleData(File fileToOpen) throws FileNotFoundException {
ArrayList<Data> handle = new ArrayList<Data>();
//clear the arraylist the second time round
handle.clear();
BufferedReader reader = new BufferedReader(new FileReader(fileToOpen));
//buffer used for scanner
int lines = 0;
//find out the value of lines here
JFrame loader = new JFrame();
JPanel loadPanel = new JPanel();
JProgressBar progressBar = new JProgressBar(0, lines);
JLabel label = new JLabel();
loader.setDefaultCloseOperation(EXIT_ON_CLOSE);
label.setText("Loading Data...");
loadPanel.add(label);
loadPanel.add(progressBar);
loader.add(loadPanel);
loader.pack();
loader.setVisible(true);
//Do a load of stuff which increments the progress bar
loader.setVisible(false);
return handle;
}
This is how the progress bar and JFrame should look:
This Is how the progress bar looks the second time:
All code:
package Default;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class ReadIn extends JFrame {
public File openFile() {
File fileToOpen = null;
JFileChooser fileChooser = new JFileChooser();
int modalToComponent = fileChooser.showOpenDialog(this);
if (modalToComponent == JFileChooser.APPROVE_OPTION) {
fileToOpen = fileChooser.getSelectedFile();
} else if (modalToComponent == JFileChooser.CANCEL_OPTION) {
System.exit(1);
}
return fileToOpen;
}
public ArrayList<Data> handleData(File fileToOpen) throws FileNotFoundException {
ArrayList<Data> handle = new ArrayList<Data>();
handle.clear();
BufferedReader reader = new BufferedReader(new FileReader(fileToOpen));
int lines = 0;
try {
while (reader.readLine() != null)
lines++;
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
Scanner sc = new Scanner(fileToOpen);
System.out.println(sc.nextLine());
System.out.println(sc.nextLine());
System.out.println("Reading Data...");
sc.nextLine();
String split = sc.nextLine();
Scanner splitter = new Scanner(split);
JDialog loader = new JDialog();
JPanel loadPanel = new JPanel();
JProgressBar progressBar = new JProgressBar(0, lines);
JLabel label = new JLabel();
loader.setDefaultCloseOperation(HIDE_ON_CLOSE);
label.setText("Loading Data...");
loadPanel.add(label);
loadPanel.add(progressBar);
loader.add(loadPanel);
loader.pack();
loader.setVisible(true);
while (splitter.hasNext()) {
String peek = splitter.next();
if (peek.equals("Timestamp")) {
peek = peek + splitter.next();
}
Data temp = new Data();
temp.setHeading(peek);
handle.add(temp);
}
while (sc.hasNextDouble()) {
progressBar.setValue(progressBar.getValue() + 1);
for (int i = 0; i < handle.size(); i++) {
handle.get(i).getValues().add(sc.nextDouble());
}
}
System.out.println("Data Loaded");
splitter.close();
sc.close();
loader.setVisible(false);
System.out.println("On EDT?: " + SwingUtilities.isEventDispatchThread());
return handle;
}
}
Without your posting a valid minimal example program, it's impossible for us to know with absolute surety the cause of your problem, the image suggests that it's a Swing threading problem, that you've got long-running code, probably here:
//Do a load of stuff which increments the progress bar
that is blocking the Swing event thread, and that this is causing the Swing GUI not to paint itself.
If so, the solution is to improve your code so that it respects the Swing event thread, so that long-running code is run in a background thread such as via a SwingWorker.
Other issues: that window you display looks to be a "dependent" window and not a main application window. If so, it should not be created as a JFrame but rather as a JDialog, either modal or non-modal depending on your needs.
Also -- why does it work the first time? Likely the first time, the code above is not run on the Swing event thread, the EDT, while the second time that code is called, it is in fact run on the EDT. You can test this by calling SwingUtilities.isEventDispatchThread() and printing out the result. e.g.,
System.out.println("On EDT?: " + SwingUtilities.isEventDispatchThread());
For example:
import java.awt.BorderLayout;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class UpdateDataEg extends JPanel {
protected static final int MAX_LINES = 200;
public UpdateDataEg() {
setPreferredSize(new Dimension(500, 400));
add(new JButton(new UpdateDataAction("Update Data", KeyEvent.VK_U)));
}
// this must be a void method as you'll get the result from a callback, not
// from this method
public void handleData(final File fileToOpen) {
int lines = 0;
// find out the value of lines here
Window window = SwingUtilities.getWindowAncestor(UpdateDataEg.this);
JDialog loader = new JDialog(window, "Progress", ModalityType.APPLICATION_MODAL);
JPanel loadPanel = new JPanel(new BorderLayout());
final JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
JLabel label = new JLabel();
label.setText("Loading Data...");
loadPanel.add(label, BorderLayout.PAGE_START);
loadPanel.add(progressBar);
loader.add(loadPanel);
loader.pack();
loader.setLocationRelativeTo(window);
final SwingWorker<ArrayList<Data>, Void> myWorker = new SwingWorker<ArrayList<Data>, Void>() {
#Override
protected ArrayList<Data> doInBackground() throws Exception {
ArrayList<Data> handle = new ArrayList<Data>();
// clear the arraylist the second time round
handle.clear();
int lines = 0;
// !! BufferedReader reader = new BufferedReader(new FileReader(fileToOpen));
// !! long code here to do calculations and place into ArrayList<Data>
// emulated by Thread.sleep
// !! set progress property here so that listener can update
while (lines < MAX_LINES) {
lines += (int) (10 * Math.random());
int myProgress = (int) ((lines * 100) / MAX_LINES);
myProgress = Math.min(100, myProgress);
setProgress(myProgress);
Thread.sleep(200);
}
return handle;
}
};
// our callback
myWorker.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getPropertyName().equals("progress")) {
int progress = myWorker.getProgress();
progressBar.setValue(progress);
} else if (pcEvt.getNewValue() == SwingWorker.StateValue.DONE) {
// if the worker has finished
loader.setVisible(false);
try {
ArrayList<Data> data = myWorker.get();
// use data here
} catch (InterruptedException | ExecutionException e) {
// TODO handle the exceptions
e.printStackTrace();
}
}
}
});
myWorker.execute();
// if the dialog is modal, this must be last
loader.setVisible(true);
// delete this call as this would be done from the callback
// loader.setVisible(false);
// delete this as this is obtained in the callback
// return handle;
}
private class UpdateDataAction extends AbstractAction {
public UpdateDataAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
handleData(new File("Foobar.txt"));
}
}
private static void createAndShowGui() {
UpdateDataEg mainPanel = new UpdateDataEg();
JFrame frame = new JFrame("UpdateDataEg");
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();
}
});
}
}
class Data {
}

How can create timer with JLabel?

I want to display in my JPanel a JLabel with timer in this mode, for example:
03:50 sec
03:49 sec
....
....
00:00 sec
So I have build this code:
#SuppressWarnings("serial")
class TimeRefreshRace extends JLabel implements Runnable {
private boolean isAlive = false;
public void start() {
Thread t = new Thread(this);
isAlive = true;
t.start();
}
public void run() {
int timeInSecond = 185
int minutes = timeInSecond/60;
while (isAlive) {
try {
//TODO
} catch (InterruptedException e) {
log.logStackTrace(e);
}
}
}
}//fine autoclass
And with this code, I can start the JLabel
TimeRefreshRace arLabel = new TimeRefreshRace ();
arLabel.start();
So I have the time in secondo for example 180 second, how can I create the timer?
Here is an example, how to build a countdown label. You can use this pattern to create your component.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.WindowConstants;
public class TimerTest {
public static void main(String[] args) {
final JFrame frm = new JFrame("Countdown");
final JLabel countdownLabel = new JLabel("03:00");
final Timer t = new Timer(1000, new ActionListener() {
int time = 180;
#Override
public void actionPerformed(ActionEvent e) {
time--;
countdownLabel.setText(format(time / 60) + ":" + format(time % 60));
if (time == 0) {
final Timer timer = (Timer) e.getSource();
timer.stop();
}
}
});
frm.add(countdownLabel);
t.start();
frm.pack();
frm.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frm.setVisible(true);
}
private static String format(int i) {
String result = String.valueOf(i);
if (result.length() == 1) {
result = "0" + result;
}
return result;
}
}
You could within your try block call the Event Dispatcher Thread (EDT) and update your UI:
try {
SwingUtils.invokeLater(new Runnable() {
#Override
public void run() {
this.setText(minutes + " left");
}
}
//You could optionally block your thread to update your label every second.
}
Optionally, you could use a Timer instead of an actual thread, so your TimerRefreshRace will have its own timer which periodically fires an event. You would then use the same code within your try-catch block to update the UI.

How to share data with two(2) SwingWorker class in Java

I have two SwingWorker class: FileLineCounterThread and FileDivisionThread
I will execute the two threads. When the lines counting thread finishes, it will pass the result to File Division thread.
I do not have an idea on how to pass the result to started thread.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public class ExecutorAndSwingWorker2 {
private JFrame frame = new JFrame();
private JButton button1;
private JButton button2;
private JButton button3;
private JButton button4;
private JPanel buttonPanel = new JPanel();
private Executor executor = Executors.newCachedThreadPool();
private javax.swing.Timer timer1;
private javax.swing.Timer timer2;
private javax.swing.Timer timer3;
private javax.swing.Timer timer4;
private Random random = new Random();
public ExecutorAndSwingWorker2() {
button1 = new JButton(" Executor + SwingWorker Thread No.1 ");
button1.setFocusable(false);
button2 = new JButton(" Executor + SwingWorker Thread No.2 ");
button3 = new JButton(" Executor + SwingWorker Thread No.3 ");
button4 = new JButton(" Executor + SwingWorker Thread No.4 ");
buttonPanel = new JPanel();
buttonPanel.setBorder(new EmptyBorder(15, 15, 15, 15));
buttonPanel.setLayout(new GridLayout(2, 2, 20, 20));
buttonPanel.add(button1);
buttonPanel.add(button2);
buttonPanel.add(button3);
buttonPanel.add(button4);
frame.setTitle("Shaking Button Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(buttonPanel);
frame.setPreferredSize(new Dimension(700, 170));
frame.setLocation(150, 100);
frame.pack();
frame.setVisible(true);
executor.execute(new ExecutorAndSwingWorker2.MyTask("startButton1")); // non on EDT
}
private void startButton1() {
System.out.println("Starting long Thread == startButton1()");
try {
Thread.sleep(15000);
} catch (InterruptedException ex) {
}
}
private void startButton2() {
System.out.println("Starting long Thread == startButton2()");
try {
Thread.sleep(17500);
} catch (InterruptedException ex) {
}
}
private void startButton3() {
System.out.println("Starting long Thread == startButton3()");
try {
Thread.sleep(12500);
} catch (InterruptedException ex) {
}
}
private void startButton4() {
System.out.println("Starting long Thread == startButton4()");
try {
Thread.sleep(20000);
} catch (InterruptedException ex) {
}
}
private void colorAction1() {
timer1 = new Timer(1000, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
random = new Random();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button1.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
button1.validate();
button1.repaint();
}
});
}
});
timer1.setDelay(500);
timer1.setRepeats(true);
timer1.start();
}
private void colorAction2() {
timer2 = new Timer(1200, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
random = new Random();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button2.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
button2.validate();
button2.repaint();
}
});
}
});
timer2.setDelay(500);
timer2.setRepeats(true);
timer2.start();
}
private void colorAction3() {
timer3 = new Timer(1400, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
random = new Random();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button3.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
button3.validate();
button3.repaint();
}
});
}
});
timer3.setDelay(500);
timer3.setRepeats(true);
timer3.start();
}
private void colorAction4() {
timer4 = new Timer(1600, new AbstractAction() {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent e) {
random = new Random();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
button4.setBackground(new Color(127 + random.nextInt(128), 127 + random.nextInt(128), 127 + random.nextInt(128)));
button4.validate();
button4.repaint();
}
});
}
});
timer4.setDelay(500);
timer4.setRepeats(true);
timer4.start();
}
private void endButton1() {
timer1.stop();
button1.setBackground(null);
System.out.println("Long Thread Ends == startButton1()");
executor.execute(new ExecutorAndSwingWorker2.MyTask("startButton3")); // non on EDT
}
private void endButton2() {
timer2.stop();
button2.setBackground(null);
System.out.println("Long Thread Ends == startButton2()");
}
private void endButton3() {
timer3.stop();
button3.setBackground(null);
System.out.println("Long Thread Ends == startButton3()");
executor.execute(new ExecutorAndSwingWorker2.MyTask("startButton2")); // non on EDT
executor.execute(new ExecutorAndSwingWorker2.MyTask("startButton4")); // non on EDT
}
private void endButton4() {
timer4.stop();
button4.setBackground(null);
System.out.println("Long Thread Ends == startButton4()");
executor.execute(new ExecutorAndSwingWorker2.MyTask("startButton1")); // non on EDT
}
private class MyTask extends SwingWorker<Void, Integer> {
private String str;
private String namePr;
private JDialog dialog = new JDialog();
MyTask(String str) {
this.str = str;
addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog, str, namePr));
}
#Override
protected Void doInBackground() throws Exception {
if (str.equals("startButton1")) {
colorAction1();
startButton1();
} else if (str.equals("startButton2")) {
colorAction2();
startButton2();
} else if (str.equals("startButton3")) {
colorAction3();
startButton3();
} else if (str.equals("startButton4")) {
colorAction4();
startButton4();
}
return null;
}
#Override
protected void process(List<Integer> progress) {
System.out.println(str + " " + progress.get(progress.size() - 1));
}
#Override
protected void done() {
if (str.equals("startButton1")) {
endButton1();
} else if (str.equals("startButton2")) {
endButton2();
} else if (str.equals("startButton3")) {
endButton3();
} else if (str.equals("startButton4")) {
endButton4();
}
}
}
private class SwingWorkerCompletionWaiter implements PropertyChangeListener {
private JDialog dialog;
private String str;
private String namePr;
SwingWorkerCompletionWaiter(JDialog dialog, String str, String namePr) {
this.dialog = dialog;
this.str = str;
this.namePr = namePr;
}
#Override
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
} else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
} else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
} else {
System.out.println("Thread Status with Name :" + str + ", Something wrong happends ");
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ExecutorAndSwingWorker2 executorAndSwingWorker = new ExecutorAndSwingWorker2();
}
});
}
}
SwingWorker.execute() is buggy and will only execute tasks serially. Use ExecutorService.execute() for concurrency:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RunnableFuture;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
public class MyFrame extends JFrame implements ActionListener {
/**
* Test Driver
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MyFrame frame = new MyFrame("Swing Concurrency Test");
frame.setVisible(true);
}
});
}
/**
* Thread Executor
* (must be explicitly shutdown, see WindowAdapter below)
*/
private final ExecutorService exec = Executors.newFixedThreadPool(2);
/**
* Button action
*/
#Override
public void actionPerformed(ActionEvent e) {
button.setEnabled(false);
textArea.append("\nStarting both tasks...\n");
// start both tasks, pass a reference to outer task
FileLineCounterThread counterTask = new FileLineCounterThread();
exec.execute(counterTask);
FileDivisionThread divisionTask = new FileDivisionThread(counterTask);
exec.execute(divisionTask);
}
/**
* Counter task
*/
private class FileLineCounterThread extends SwingWorker<Long, String> {
private String template = "[FileLineCounterThread] %s\n";
#Override
protected Long doInBackground() throws Exception {
// do some work
publish("started...");
Thread.sleep(10000);
// return the result
return 42L;
}
#Override
protected void process(List<String> chunks) {
for (String chunk : chunks) {
textArea.append(String.format(template, chunk));
}
}
#Override
protected void done() {
try {
textArea.append(String.format(
template, "complete. Counted: " + get()));
}
catch (Exception e) {
// catch any exceptions thrown during execution
e.printStackTrace();
}
}
}
/**
* File Division task
*/
private class FileDivisionThread extends SwingWorker<String, String> {
private RunnableFuture<Long> counterTask;
private String template = " [FileDivisionThread] %s\n";
public FileDivisionThread(RunnableFuture<Long> counterTask) {
this.counterTask = counterTask;
}
#Override
protected String doInBackground() throws Exception {
// do some initial work
publish("started...");
Thread.sleep(2000);
// wait for other task to complete and get result
publish("Waiting for line counter to finish...");
long numLines = counterTask.get();
publish("Line count received: " + numLines);
// do the rest of the work and return result
Thread.sleep(5000);
return "complete.";
}
#Override
protected void process(List<String> chunks) {
for (String chunk : chunks) {
textArea.append(String.format(template, chunk));
}
}
#Override
protected void done() {
try {
textArea.append(String.format(template, get()));
button.setEnabled(true);
}
catch (Exception e) {
// catch any exceptions thrown during execution
e.printStackTrace();
}
}
}
/////////////////////////
//// GUI Boilerplate ////
/////////////////////////
private JScrollPane scroller = new JScrollPane();
private JTextArea textArea = new JTextArea();
private JButton button = new JButton("Start");
public MyFrame(String windowTitle) {
super(windowTitle);
initComponents();
}
private void initComponents() {
addWindowListener(new WindowAdapter() {
#Override
public void windowClosed(WindowEvent e) {
exec.shutdownNow();
System.exit(0);
}
});
button = new JButton("Start");
button.addActionListener(this);
textArea = new JTextArea();
textArea.setColumns(35);
textArea.setRows(15);
scroller.setViewportView(textArea);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridBagLayout());
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.insets = new Insets(10, 0, 0, 0);
getContentPane().add(button, gridBagConstraints);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new Insets(10, 10, 10, 10);
getContentPane().add(scroller, gridBagConstraints);
pack();
}
}
PipedReader/Writer for character data & PipedInput/OutputStream for binary data
in java.io.
Regards,
Stéphane
never hands up, never surrender its possible with Executor and SwingWorker
1/ bug for Executor and SwingWorker
2/ hold and check number of thread started by Executor and live SwingWorkers threads with intentions to avoid caught above mentioned bug
3/ check maximum numbers for Executor or restict that to final munber
EDIT changed by OP's requirements
import java.beans.*;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class ExecutorAndSwingWorker1 {
private static Executor executor = Executors.newCachedThreadPool();
private static void startButton1() {
System.out.println("Starting long Tread == startButton1()");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
private static void startButton2() {
System.out.println("Starting long Tread == startButton2()");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
}
}
private static void startButton3() {
System.out.println("Starting long Tread == startButton3()");
try {
Thread.sleep(1500);
} catch (InterruptedException ex) {
}
}
private static void startButton4() {
System.out.println("Starting long Tread == startButton4()");
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
}
private static void endButton1() {
System.out.println("Long Tread Ends == startButton1()");
executor.execute(new ExecutorAndSwingWorker1.MyTask("startButton3")); // non on EDT
}
private static void endButton2() {
System.out.println("Long Tread Ends == startButton2()");
executor.execute(new ExecutorAndSwingWorker1.MyTask("startButton4")); // non on EDT
}
private static void endButton3() {
System.out.println("Long Tread Ends == startButton3()");
}
private static void endButton4() {
System.out.println("Long Tread Ends == startButton3()");
}
private static class MyTask extends SwingWorker<Void, Integer> {
private String str;
private String namePr;
private JDialog dialog = new JDialog();
MyTask(String str) {
this.str = str;
addPropertyChangeListener(new SwingWorkerCompletionWaiter(dialog, str, namePr));
}
#Override
protected Void doInBackground() throws Exception {
if (str.equals("startButton1")) {
startButton1();
} else if (str.equals("startButton2")) {
startButton2();
} else if (str.equals("startButton3")) {
startButton3();
} else if (str.equals("startButton4")) {
startButton4();
}
return null;
}
#Override
protected void process(List<Integer> progress) {
System.out.println(str + " " + progress.get(progress.size() - 1));
}
#Override
protected void done() {
if (str.equals("startButton1")) {
endButton1();
} else if (str.equals("startButton2")) {
endButton2();
} else if (str.equals("startButton3")) {
endButton3();
} else if (str.equals("startButton4")) {
endButton4();
}
}
}
private static class SwingWorkerCompletionWaiter implements PropertyChangeListener {
private JDialog dialog;
private String str;
private String namePr;
SwingWorkerCompletionWaiter(JDialog dialog, String str, String namePr) {
this.dialog = dialog;
this.str = str;
this.namePr = namePr;
}
#Override
public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.DONE == event.getNewValue()) {
System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
} else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.PENDING == event.getNewValue()) {
System.out.println("Thread Status with Mame :" + str + ", SwingWorker Status is " + event.getNewValue());
} else if ("state".equals(event.getPropertyName()) && SwingWorker.StateValue.STARTED == event.getNewValue()) {
System.out.println("Thread Status with Name :" + str + ", SwingWorker Status is " + event.getNewValue());
} else {
System.out.println("Thread Status with Name :" + str + ", Something wrong happends ");
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
executor.execute(new ExecutorAndSwingWorker1.MyTask("startButton1")); // non on EDT
executor.execute(new ExecutorAndSwingWorker1.MyTask("startButton2")); // non on EDT
}
});
}
private ExecutorAndSwingWorker1() {
}
}
I am not sure this is a solution you should use, and it undermines the simplicity and safety you get from using SwingWorker, but I'll mention it for completeness.
Put two fields where both threads can see them: one boolean, called hasValue, initialized to false, and one int (or long) called countValue. Both must be declared as volatile. When the counter thread is done, put the count in countValue. Then set hasValue to true. The division thread can then check `hasValue' periodically and grab the count when it is available.
If the division is providing values that will be more accurate once it gets the count, this will do. More likely, it is doing some work, then waiting for the count. In this case, set up a third field called countMonitor, defined as final Object. When it finishes the initial work, have it check hasValue. If it's true, grab the value and continue. If it's false, call the wait method on countMonitor and continue when notified. The counter thread, when done, should always call the notifyAll method on countMonitor after putting values in hasValue and countValue.
I've left out a bit here. The javadoc for Object will tell you about needed synchronization and checked exceptions. Your design is straightforward enough that you won't be troubled with the usual supernatural horror stories multi-threading generates. I hope. But you might want to do a bit of research if you go this route. (If you repeat the whole process in the same session, you will definitely want to do a lot of research.)

Categories