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 ();
}
}
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 am making a stock market simulator and I keep getting an error from each of my 3 classes the first class MYOSM the error is:
"Cannot find symbol- class stock"
The second class MarketTable outputs:
Cannot find MarketDataModel
And the third class MarketDataModel outputs the error:
Cannot find symbol- class stock
Have I gone wrong somewhere in my code I have checked it multiple times and I can't seem to locate it.
Here is my code:
MYOSM class:
import javax.swing.*;
import java.awt.*;
import java.util.*;
public class MYOSM extends JFrame implements Runnable {
Stock[] market = {
new Stock("JTree", 14.57),
new Stock("JTable", 17.44),
new Stock("JList", 16.44),
new Stock("JButton", 7.21),
new Stock("JComponent", 27.40)
};
boolean monitor;
Random rg = new Random();
Thread runner;
public MYOSM() {
// Not meant to be shown as a real frame
super("Thread only version . . .");
runner = new Thread(this);
runner.start();
}
public MYOSM(boolean monitorOn) {
super("Stock Market Monitor");
setSize(400, 100);
setDefaultCloseOperation(EXIT_ON_CLOSE);
monitor = monitorOn;
getContentPane().add(new JLabel("Trading is active. " +
"Close this window to close the market."),
BorderLayout.CENTER);
runner = new Thread(this);
runner.start();
}
public void run() {
while(true) {
int whichStock = Math.abs(rg.nextInt()) % market.length;
double delta = rg.nextDouble() - 0.4;
market[whichStock].update(delta);
if (monitor) {
market[whichStock].print();
}
try {
Thread.sleep(1000);
}
catch(InterruptedException ie) {
}
}
}
public Stock getQuote(int index) {
return market[index];
}
// This method returns the list of all the symbols in the market table
public String[] getSymbols() {
String[] symbols = new String[market.length];
for (int i = 0; i < market.length; i++) {
symbols[i] = market[i].symbol;
}
return symbols;
}
public static void main(String args[]) {
MYOSM myMarket = new MYOSM(args.length > 0);
myMarket.setVisible(true);
}
}
MarketTable class:
import java.awt.*;
import javax.swing.*;
public class MarketTable extends JFrame {
public MarketTable() {
super("Dynamic Data Test");
setSize(300, 200);
setDefaultCloseOperation(EXIT_ON_CLOSE);
MarketDataModel mdm = new MarketDataModel(5);
mdm.setStocks(new int[] { 0, 1, 2 });
JTable jt = new JTable(mdm);
JScrollPane jsp = new JScrollPane(jt);
getContentPane().add(jsp, BorderLayout.CENTER);
}
public static void main(String args[]) {
MarketTable mt = new MarketTable();
mt.setVisible(true);
}
}
MarketDataModel class:
import javax.swing.table.*;
import javax.swing.*;
public class MarketDataModel extends AbstractTableModel
implements Runnable {
Thread runner;
MYOSM market;
int delay;
public MarketDataModel(int initialDelay) {
market = new MYOSM();
delay = initialDelay * 1000;
Thread runner = new Thread(this);
runner.start();
}
Stock[] stocks = new Stock[0];
int[] stockIndices = new int[0];
String[] headers = {"Symbol", "Price", "Change", "Last updated"};
public int getRowCount() { return stocks.length; }
public int getColumnCount() { return headers.length; }
public String getColumnName(int c) { return headers[c]; }
public Object getValueAt(int r, int c) {
switch(c) {
case 0:
return stocks[r].symbol;
case 1:
return new Double(stocks[r].price);
case 2:
return new Double(stocks[r].delta);
case 3:
return stocks[r].lastUpdate;
}
throw new IllegalArgumentException("Bad cell (" + r + ", " + c +")");
}
public void setDelay(int seconds) { delay = seconds * 1000; }
public void setStocks(int[] indices) {
stockIndices = indices;
updateStocks();
fireTableDataChanged();
}
public void updateStocks() {
stocks = new Stock[stockIndices.length];
for (int i = 0; i < stocks.length; i++) {
stocks[i] = market.getQuote(stockIndices[i]);
}
}
public void run() {
while(true) {
updateStocks();
fireTableRowsUpdated(0, stocks.length - 1);
try { Thread.sleep(delay); }
catch(InterruptedException ie) {}
}
}
}
You're missing a Stock class there. Should be something like this I suppose:
public class Stock {
public Stock(String string, double d) {
this.symbol = string;
this.price = d;
}
public String symbol;
public double price;
public double delta;
public String lastUpdate;
public void print() {
System.out.println(this);
}
public void update(double delta2) {
this.delta = delta2;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Stock [symbol=").append(symbol).append(", price=").append(price).append(", delta=").append(delta).append(", lastUpdate=")
.append(lastUpdate).append("]");
return builder.toString();
}
}
Or is it just in a different package than those classes?
Also it's weird your error message has "stock" in lowercase.
With the Stock class added as above, I have managed to start your code, but I'm not sure what it was supposed to be doing. I must note it is poorly written in general, with some basic mistakes like usage of default package.
I have a program that estimates the value of PI every million trials. However, I want the program to be paused when I click on pause and to resumes when I click on run, using wait() and notify().
I must use multiple threads as well as a Boolean as a signal of where should it pause and run, but I do not know how. I am confused.
Any ideas?
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Ex03 extends JFrame implements Runnable, ActionListener {
int n = 0;
int c = 0;
double Pi;
int change = 1000000;
boolean runing = true;
JLabel actualpi = new JLabel("The Actual value of PI " + Math.PI);
JLabel estimation = new JLabel("Current Estimate: ");
JLabel tri = new JLabel("Number Of Trials: " + n);
JButton run = new JButton("Run");
JButton pause = new JButton("Pause");
public Ex03() {
super("Ex 03");
setLayout(new GridLayout(4, 1));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setVisible(true);
add(actualpi);
add(estimation);
add(tri);
add(run);
run.addActionListener(this);
pause.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread(this);
if (e.getSource() == run) {
thread.start();
remove(run);
add(pause);
} else if (e.getSource() == pause) {
remove(pause);
add(run);
try {
thread.wait();
} catch (InterruptedException e1) {
}
}
}
public void run() {
n++;
while (runing) {
double x = Math.random();
double y = Math.random();
if (((x * x) + (y * y)) <= 1)
c++;
n++;
Pi = (4.0 * (double) c / n);
if (n == change) {
estimation.setText("Current Estimate: " + Pi);
tri.setText("Number Of Trials: " + n);
change = change + 1000000;
}
try {
Thread.sleep(0);
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
new Ex03();
}
}
Here is example how to do it in console application using Semaphore:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String[] args) throws Exception {
PiThread piThread = new PiThread();
piThread.start();
BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in));
String cmd;
while ((cmd = bufferRead.readLine()) != null) {
System.out.println("write p to pause, r to resume, s to stop");
switch (cmd) {
case "p":
piThread.pauseComputation();
break;
case "r":
piThread.resumeComputation();
break;
case "s":
piThread.stopComputation();
piThread.join();
return;
}
}
}
public static class PiThread extends Thread {
private volatile boolean stop = false;
private volatile int total = 0;
private volatile int insideCircle = 0;
private Semaphore semaphore = new Semaphore(1, true);
#Override
public void run() {
while (!stop) {
for (int i = 0; i < 1000; i++) {
double x = Math.random();
double y = Math.random();
if (((x * x) + (y * y)) <= 1)
insideCircle++;
total++;
}
// using semaphores is slow
try {
// not to garbage stdout
Thread.sleep(100);
semaphore.acquire();
semaphore.release();
}
catch (InterruptedException e) {
// exit
return;
}
System.out.println("pi: " + getPiApproximation());
System.out.flush();
}
}
public void pauseComputation() {
try {
semaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void resumeComputation() {
semaphore.release();
}
public void stopComputation() {
stop = true;
}
public double getPiApproximation() {
return 4*(double)insideCircle / (double)total;
}
}
}
It may not be the best way to handle this in GUI but will give you a good start. The main idea is to use Semaphore to block worker thread. For this to work you must always call pauseComputation before resumeComputation otherwise there will be deadlock. After you call stopComputation you cannot resumeComputation or pauseComputation.
When I run this class(Try class), it calls the Stacker class completely(with full functions), but when I use other class(I used JFrame with button that has actionlistener that calls Stacker class) to run this(Stacker class), the JFrame(Stacker class) will pop-up but empty and I can't close the program.
I tried to run this(Stacker) from other class like this:
public class Try {
/**
* #param args
*/
public static void main(String[] args) {
run();
}
public static void run(){
new Stacker();
}
}
The Stacker class ran fully(I can interact with it). But when I tried to call the stacker class from an actionlistener of a button in JFrame, it's blank and can't be closed.
Please help me.
here are my codes for the Stacker class:
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Stacker extends JFrame implements KeyListener {
int iteration = 1;
static double time = 200;
static int last = 0;
static int m = 10;
static int n = 20;
JButton b[][];
static int length[] = {5,5};
static int layer = 19;
static int deltax[] = {0,0};
static boolean press = false;
static boolean forward = true;
static boolean start = true;
JPanel panel;
public static void main (String[] args) {
Stacker stack = new Stacker();
stack.setVisible(true);
}
public Stacker() {
panel = new JPanel();
panel.setLayout(new GridLayout(20,10));
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
b = new JButton [m][n];
for (int y = 0;y<n;y++) {
for (int x = 0;x<m;x++) {
b[x][y] = new JButton(" ");
b[x][y].setBackground(Color.white);
b[x][y].setBorderPainted(false);
panel.add(b[x][y]);
b[x][y].setEnabled(true);
}//end inner for
}
setSize(390, 560);
setLayout(new GridBagLayout());
add(panel);
setFocusable(true);
addKeyListener(this);
pack();
setVisible(true);
go();
}
public void go() {
int tmp = 0;
Component temporaryLostComponent = null;
do {
if (forward == true) {
forward();
} else {
back();
}
if (deltax[1] == 10-length[1]){
forward = false;
} else if (deltax[1] == 0){
forward = true;
}
draw();
try {
Thread.sleep((long) time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}while(press == false);
if (layer>12) {
time= 150-(iteration*iteration*2-iteration);
} else {
time = time - 2.2;
}
iteration++;
layer--;
press = false;
tmp = check();
length[0] = length[1];
length[1] = tmp;
if (layer == -1) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Congratulations! You beat the game!");
}
if (length[1] <= 0) {
JOptionPane.showMessageDialog(temporaryLostComponent, "Game over! You reached line "+(18-layer)+"!");
System.exit(0);
}
last = deltax[1];
start = false;
go();
}
public int check() {
if (start == true) {
return length[1];
} else if (last<deltax[1]) {
if (deltax[1]+length[1]-1 <= last+length[0]-1) {
return length[1];
} else {
return length[1]-Math.abs((deltax[1]+length[1])-(last+length[0]));
}
} else if (last>deltax[1]) {
return length[1]-Math.abs(deltax[1]-last);
} else {
return length[1];
}
}
public void forward() {
deltax[0] = deltax[1];
deltax[1]++;
}
public void back() {
deltax[0] = deltax[1];
deltax[1]--;
}
public void draw() {
for (int x = 0;x<length[1];x++) {
b[x+deltax[0]][layer].setBackground(Color.white);
}
for (int x = 0;x<length[1];x++) {
b[x+deltax[1]][layer].setBackground(Color.BLUE);
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
press = true;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}
Your do-while loop and thread sleep look suspicious in that it is running on the Swing event thread tying it up. The problem is that these longer running tasks lock the Swing Event Dispatch Thread, or EDT, the one thread that is responsible for all Swing graphics and user interactions, preventing your application from drawing itself and all of its widgets and prevents the application from responding to any user input.
Likely the application ran in isolation because the issues that I identified above ran off of the EDT, and doing this code wise is one possible solution to your problem, but I'm betting that there are better solutions available that we can help you with if you tell us more about the details of your problem.
Also you appear to be using recursion in a dangerous way having the go method call itself. Can't really say more since I'm on a cell phone.
Firstly, I would replace:this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
with the following:
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
as DISPOSE_ON_CLOSE is a static/class variable.
In fact, I am surprised that your IDE did not pick that up (unless, you aren't using an IDE).
SwingWorker worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
Stacker stack = new Stacker();
stack.setVisible(true);
return null;
}
};
worker.execute();
after using this method, when I call Stacker class from another JFrame, the game executes perfectly. Just like when calling Stacker class from Try class.
Thank you for all the responses I had. :D
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.