I have JList using DefaultListModel to update the strings in list in UI , returned by a class as shown
class ResponseGiver implements Callable<Future>{
int i;
//Constructor to initialize i
String call(){
...............
...............
return i;
}
and i have other class that will update the results obtained from above
class Viewer {
ExecutorService es = new Executors.newFixedThreadPool(10);
List<Future<String>> futures = new ArrayList<Future<String>>();
for(int i =0;i<10;i++)
{
futures.add(new ResponseGiver(i));
}
for(Future<String> x : futures) //loop 2nd will be called 10 times
{
String p = x.get();
//update GUI with p
}
Now the question is, suppose in loop 2nd , in the 5th loop, the get() function takes some time say 10 seconds, and during the mean time, the other futures from 6th to 10th have their result ready.
So my screen would wait for 5th result, even 6th to 10th are ready.
I want my screen to be updated as soon as any of the 10 futures return the result.
Using the standard API, you can use a ExecutorCompletionService. It allows submitting Callable instances and returns Future objects, just like a normal ExecutorService. But it additionally allows obtaining the Future objects in the order in which they are finished: Using the ExecutorCompletionService#take() method. This method blocks until a new Future is available. You can imagine this as the Futures being placed into a blocking queue.
You can start a thread that consumes these Future objects from the completion service. The result of such a Future may then be used to update the GUI. (Note that this update, in turn, has to be done on the Event Dispatch Thread again, using SwingUtilities.invokeLater).
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ExecutorCompletionServiceTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
JButton button = new JButton("Run");
f.getContentPane().add(button, BorderLayout.NORTH);
final DefaultListModel<String> listModel = new DefaultListModel<String>();
JList<String> list = new JList<String>(listModel);
f.getContentPane().add(new JScrollPane(list), BorderLayout.CENTER);
final Callback callback = new Callback()
{
#Override
public void call(final String result)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
listModel.addElement(result);
}
});
}
};
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
createTasks(callback);
}
});
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
interface Callback
{
void call(String result);
}
private static Random random = new Random(0);
static class ResponseGiver implements Callable<String>
{
private int i;
ResponseGiver(int i)
{
this.i = i;
}
#Override
public String call()
{
int delayMS = 250 + random.nextInt(500);
// Simulate a longer delay for task 5
if (i == 5)
{
delayMS += 3000;
}
try
{
System.out.println("For "+i+" waiting "+delayMS+" ms");
Thread.sleep(delayMS);
System.out.println("For "+i+" waiting "+delayMS+" ms DONE");
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
return String.valueOf(i);
}
}
private static void createTasks(final Callback callback)
{
ExecutorService executorService = Executors.newFixedThreadPool(10);
final CompletionService<String> executorCompletionService =
new ExecutorCompletionService<String>(executorService);
final int n = 10;
for (int i = 0; i < 10; i++)
{
executorCompletionService.submit(new ResponseGiver(i));
}
Thread thread = new Thread(new Runnable()
{
#Override
public void run()
{
processResults(executorCompletionService, n, callback);
}
});
thread.start();
}
private static void processResults(
CompletionService<String> completionService, int n, Callback callback)
{
for (int i = 0; i < n; i++)
{
try
{
Future<String> future = completionService.take();
String result = future.get();
if (result != null)
{
callback.call(result);
}
System.out.println("Processed "+(i+1)+" of "+n+" results");
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
}
}
Just use ListenableFuture from guava. It's much more convenient.
Related
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();
I have to make a DieTester for school. One that rolls the dice 100 times and then puts the output in a Table Chart and another table.
The problem is that my Thread wont sleep with the time that is set by the Slider.
Here my DieTester:
package sample.Controllers;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.RunnableFuture;
public class DieTester implements Runnable{
private Thread t;
private String Threadname;
List<Integer> List = new ArrayList();
Random rand = new Random();
long l;
public DieTester(String name){
Threadname = name;
}
public void run() {
for (int n = 0; n < 100; n++) {
try {
Thread.sleep(getTime());
List.add(rand.nextInt(6) + 1);
System.out.println(List.get(n));
} catch (InterruptedException e) {
System.out.println("Error");
}
}
}
public void start(){
if (t == null)
{
t = new Thread (this, Threadname);
t.start ();
}
}
public void setTime(double SliderTime){
l = (long) SliderTime;
}
public long getTime(){
return l;
}
}
Here the controller:
package sample.Controllers;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Slider;
public class Controller {
DieTester dice = new DieTester("Time");
double time=0;
EventHandler e = new EventHandler() {
#Override
public void handle(Event event) {
time = TimeSlider.getValue();
}
};
#FXML
Slider TimeSlider = new Slider(50, 2000, 50);
#FXML
public void HandlePauseResumeAction(){
}
#FXML
public void HandleStartAction(){
DieTester die = new DieTester("Start");
die.start();
}
#FXML
public void HandleSlider(){
TimeSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
time = TimeSlider.getValue() * 20;
//System.out.println(time);
dice.setTime(time);
});
System.out.println(dice.getTime());
}
}
The slider and everything is set up properly. And if I call the getTime() it puts out the time properly, but the Thread isn't sleeping or something.
This is a mutable shared variable :
long l
Threads access it concurrently, (one reads, one writes), yet it doesn't have proper synchronization, so writes of one thread are not guaranteed to be seen by the other thread.
On top of that, l is initialized to 0, and odds are the spawned thread has raced through 100 loops, without really sleeping, before the first property change event happens.
Ok guys so I figured it out, this is what I did
#FXML
public void HandleStartAction(){
start();
}
public void run(){
for(int n = 0; n < 100; n++){
try {
if(!suspend){
Thread.sleep((long)TimeSlider.getValue() * 20);
List.add(rand.nextInt(6) + 1);
System.out.println(List.get(n));
}else{
}
} catch (InterruptedException e) {
System.out.println("Error");
}
}
}
public void start(){
if (t == null)
{
t = new Thread (this, "Start");
t.start ();
}
}
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.
We are using foxtrot package for stop freeze the swing application.
But in this below code it make a deadlock.
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import foxtrot.Task;
import foxtrot.Worker;
public class FoxtrotExample extends JFrame {
public static void main(String[] args) {
FoxtrotExample example = new FoxtrotExample();
example.setVisible(true);
}
boolean st = true;
public FoxtrotExample() {
super("Foxtrot Example");
final JButton button = new JButton("Take a nap !");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Start..");
button.setText("Sleeping...");
String text = null;
try {
text = (String) Worker.post(new Task() {
public Object run() throws Exception {
System.out.println("Inside Worker 1");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
System.out.println("Inside invokeLater");
Worker.post(new Task() {
#Override
public Object run()
throws Exception {
System.out.println("Inside Worker 2");
st = false;
return null;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
while (st) {
System.out.println("Inside the loop..");
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
return "Slept !";
}
});
} catch (Exception x) {
}
button.setText(text);
System.out.println("Finished.....");
}
});
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new GridBagLayout());
c.add(button);
setSize(300, 200);
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
Dimension size = getSize();
int x = (screen.width - size.width) >> 1;
int y = (screen.height - size.height) >> 1;
setLocation(x, y);
}
}
If use ConcurrentWorker this will work fine.Can any one explane this.
I am bit confuse how EDT behave here ?
This is the result of my program.
Start 1st worker
In the loop
Start invoke later
In the loop
In the loop
In the loop
In the loop
......
It start the 1st worker.Then part of the code is in invokeLater.So request is enqued in the event queue and start the loop.Later execute the invokeLater but not execute the 2nd worker because first worker still doing some work.Since worker are ruining one after another and it runs on a single worker queue 2nd worker cannot execute and deadlock comes.
Thanks to MadProgrammer i understood this.Hope this is correct.
I have code:
import java.awt.Dimension;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test2 {
JFrame frame = null;
JPanel panel = null;
JButton button = null;
Task task = null;
Indicator indicator = null;
Runnable computation;
public static void main(String[] args) {
new Test2().start();
}
public void start() {
SwingUtilities.invokeLater(new Dialog());
}
private void process1() {
int result = 0;
for (int i=0; i<100000; i++) {
result = (int) Math.ceil(++result + Math.sqrt(result));
System.out.println("proc1 " + result);
}
}
private void process2() {
int result = 0;
for (int i=0; i<100000; i++) {
result = (int) Math.ceil(++result + Math.sqrt(result)*500);
System.out.println("proc2 " + result);
}
}
private class Computation implements Runnable {
public void run() {
process1();
task.setProgress(2);
process2();
task.setProgress(3);
}
}
private class Dialog implements Runnable {
public Dialog() {
}
public void run() {
frame = new JFrame("Test");
panel = new JPanel();
panel.setPreferredSize(new Dimension(300, 200));
frame.getContentPane().add(panel);
button = new JButton("b1");
panel.add(button);
indicator = new Indicator();
task = new Task();
task.addObserver(indicator);
frame.pack();
frame.setVisible(true);
computation = new Computation();
SwingUtilities.invokeLater(computation);
}
}
private class Task extends Observable {
int progress;
public Task() {
}
public void setProgress(int progress) {
this.progress = progress;
setChanged();
notifyObservers();
}
public int getProgress() {
return progress;
}
}
private class Indicator implements Observer {
#Override
public void update(Observable arg0, Object arg1) {
button.setText(((Task)arg0).getProgress()+"");
}
}
}
So I have two time-consuming operations (process1 and process2). My aim is after process1 is complete, update swing-button (see task.setProgress method).
Problem consists in that update is performed after process1() and process2() are completed.
..update is performed after process1() and process2() are completed.
Don't perform long running tasks on the EDT, see Concurrency in Swing for details. One way to achieve that is to use a SwingWorker.
..if I use two SwingWorkers for performing process1() and process2(), then order of their execution is unpredictable. I need process2() follows by process1(). How I can obtain this?
Call both methods in the doInBackground() method of 1 SwingWorker, calling SwingWorker.setProgress(int) with the appropriate values at the appropriate times. E.G.
... doInBackground() {
setProgress(0);
process1();
setProgress(50);
process2();
setProgress(100);
}