I am trying the learn how to use executorservice of Java,
I was reading the following discussion Java thread simple queue
In this there is a sample example
ExecutorService service = Executors.newFixedThreadPool(10);
// now submit our jobs
service.submit(new Runnable() {
public void run() {
do_some_work();
}
});
// you can submit any number of jobs and the 10 threads will work on them
// in order
...
// when no more to submit, call shutdown
service.shutdown();
// now wait for the jobs to finish
service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
I tried implementing this solution, for this i have create one form and placed start and stop button but the problem which i am facing is, if i call this process on start button, it will hang complete form and we need to wait until all process is completed.
I also tried to read the following https://www3.ntu.edu.sg/home/ehchua/programming/java/J5e_multithreading.html
but till now i am not able to understand how to make it work, as after clicking the start button, i should get access back, suppose i want to stop the process.
can someone please guide me in right direction.
Thanks
To make my situation more clear, i am adding the code which i am testing.
Problems
1) complete form remain frozen when program execute.
2) Progressbar dont work, will display status only when all process is completed.
private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {
TestConneciton();
}
private void btnStopActionPerformed(java.awt.event.ActionEvent evt) {
flgStop = true;
}
private static final int MYTHREADS = 30;
private boolean flgStop = false;
public void TestConneciton() {
ExecutorService executor = Executors.newFixedThreadPool(MYTHREADS);
String[] hostList = { "http://crunchify.com", "http://yahoo.com",
"http://www.ebay.com", "http://google.com",
"http://www.example.co", "https://paypal.com",
"http://bing.com/", "http://techcrunch.com/",
"http://mashable.com/", "http://thenextweb.com/",
"http://wordpress.com/", "http://wordpress.org/",
"http://example.com/", "http://sjsu.edu/",
"http://ebay.co.uk/", "http://google.co.uk/",
"http://www.wikipedia.org/",
"http://en.wikipedia.org/wiki/Main_Page" };
pbarStatus.setMaximum(hostList.length-1);
pbarStatus.setValue(0);
for (int i = 0; i < hostList.length; i++) {
String url = hostList[i];
Runnable worker = new MyRunnable(url);
executor.execute(worker);
}
executor.shutdown();
// Wait until all threads are finish
// while (!executor.isTerminated()) {
//
// }
System.out.println("\nFinished all threads");
}
public class MyRunnable implements Runnable {
private final String url;
MyRunnable(String url) {
this.url = url;
}
#Override
public void run() {
String result = "";
int code = 200;
try {
if(flgStop == true)
{
//Stop thread execution
}
URL siteURL = new URL(url);
HttpURLConnection connection = (HttpURLConnection) siteURL
.openConnection();
connection.setRequestMethod("GET");
connection.connect();
code = connection.getResponseCode();
pbarStatus.setValue(pbarStatus.getValue()+1);
if (code == 200) {
result = "Green\t";
}
} catch (Exception e) {
result = "->Red<-\t";
}
System.out.println(url + "\t\tStatus:" + result);
}
}
Per the ExecutorService API, this blocks:
service.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
API quote:
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
If you don't want this blocking your current thread, then perhaps you should call it in a different thread. Also, is yours is a Swing application, then consider using a SwingWorker, which I believe uses ExecutorServices "under the hood".
Based on your latest bit of code, I'd use
A SwingWorker to manage all the background threads.
I'd give the SwingWorker an ExecutorService
and also an ExecutorCompletionService that was initialized with the ExecutorService as this would allow me to grab task results as they're being completed
I'd fill it with Callables, not Runnables, since this would allow the task to return something, perhaps a String to indicate progress.
I'd set the SwingWorker's progress property to (100 * taskCount) / totalTaskCount and have my JProgressBar go from 0 to 100.
I'd then use the SwingWorker's publish/process method pairs extract the Strings returned by the callable.
I'd listen to the progress of the SwingWorker in my GUI with a PropertyChangeListener
And then make changes to the GUI from within this listener.
I'd change if (code == 200) { to if (code == HttpURLConnection.HTTP_OK) { to avoid magic numbers.
The JButton's Action would disable itself, then create a new SwingWorker object, add the worker's ProperChangeListener to the worker, then execute the worker.
For example
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
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.TimeUnit;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
#SuppressWarnings("serial")
public class SwingExecutorCompletionService extends JPanel {
public static final int MYTHREADS = 10;
private static final int LIST_PROTOTYPE_SIZE = 120;
private static final String LIST_PROTOTYPE_STRING = "%" + LIST_PROTOTYPE_SIZE + "s";
public static final String[] HOST_LIST = {
"http://crunchify.com",
"http://yahoo.com",
"http://www.ebay.com",
"http://google.com",
"http://www.example.co",
"https://paypal.com",
"http://bing.com/",
"http://techcrunch.com/",
"http://mashable.com/",
"http://thenextweb.com/",
"http://wordpress.com/",
"http://wordpress.org/",
"http://example.com/",
"http://sjsu.edu/",
"http://ebay.co.uk/",
"http://google.co.uk/",
"http://www.wikipedia.org/",
"http://en.wikipedia.org/wiki/Main_Page" };
private JProgressBar pbarStatus = new JProgressBar(0, 100);
private JButton doItButton = new JButton(new DoItAction("Do It", KeyEvent.VK_D));
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> resultList = new JList<>(listModel);
public SwingExecutorCompletionService() {
resultList.setVisibleRowCount(10);
resultList.setPrototypeCellValue(String.format(LIST_PROTOTYPE_STRING, ""));
resultList.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
add(pbarStatus);
add(doItButton);
add(new JScrollPane(resultList, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
}
public void addToCompletionList(String element) {
listModel.addElement(element);
}
public void setStatusValue(int progress) {
pbarStatus.setValue(progress);
}
class DoItAction extends AbstractAction {
public DoItAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
DoItWorker worker = new DoItWorker(HOST_LIST, MYTHREADS);
SwingExecutorCompletionService gui = SwingExecutorCompletionService.this;
PropertyChangeListener workerListener = new WorkerChangeListener(gui, this);
worker.addPropertyChangeListener(workerListener);
worker.execute();
}
}
private static void createAndShowGui() {
SwingExecutorCompletionService mainPanel = new SwingExecutorCompletionService();
JFrame frame = new JFrame("Swing ExecutorCompletionService");
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 MyCallable implements Callable<String> {
private static final String RED = "->Red<-";
private static final String GREEN = "Green";
private final String url;
private volatile boolean flgStop;
MyCallable(String url) {
this.url = url;
}
#Override
public String call() throws Exception {
String result = "";
int code = HttpURLConnection.HTTP_OK;
try {
// if(flgStop == true)
if (flgStop) {
// Stop thread execution
}
URL siteURL = new URL(url);
HttpURLConnection connection = (HttpURLConnection) siteURL
.openConnection();
connection.setRequestMethod("GET");
connection.connect();
code = connection.getResponseCode();
// No don't set the prog bar in a background thread!
// !! pbarStatus.setValue(pbarStatus.getValue()+1);
// avoid magic numbers
if (code == HttpURLConnection.HTTP_OK) {
result = GREEN;
}
} catch (Exception e) {
result = RED;
}
return String.format("%-40s %s", url + ":", result);
}
}
class WorkerChangeListener implements PropertyChangeListener {
private Action action;
private SwingExecutorCompletionService gui;
public WorkerChangeListener(SwingExecutorCompletionService gui, Action button) {
this.gui = gui;
this.action = button;
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
DoItWorker worker = (DoItWorker)evt.getSource();
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
action.setEnabled(true);
try {
worker.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} else if (DoItWorker.INTERMEDIATE_RESULT.equals(evt.getPropertyName())) {
gui.addToCompletionList(evt.getNewValue().toString());
} else if ("progress".equals(evt.getPropertyName())) {
gui.setStatusValue(worker.getProgress());
}
}
}
class DoItWorker extends SwingWorker<Void, String> {
public static final String INTERMEDIATE_RESULT = "intermediate result";
private static final long TIME_OUT = 5;
private static final TimeUnit UNIT = TimeUnit.MINUTES;
private String intermediateResult;
private ExecutorService executor;
private CompletionService<String> completionService;
private String[] hostList;
public DoItWorker(String[] hostList, int myThreads) {
this.hostList = hostList;
executor = Executors.newFixedThreadPool(myThreads);
completionService = new ExecutorCompletionService<>(executor);
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < hostList.length; i++) {
String url = hostList[i];
Callable<String> callable = new MyCallable(url);
completionService.submit(callable);
}
executor.shutdown();
for (int i = 0; i < hostList.length; i++) {
String result = completionService.take().get();
publish(result);
int progress = (100 * i) / hostList.length;
setProgress(progress);
}
executor.awaitTermination(TIME_OUT, UNIT);
setProgress(100);
return null;
}
#Override
protected void process(List<String> chunks) {
for (String chunk : chunks) {
setIntermediateResult(chunk);
}
}
private void setIntermediateResult(String intermediateResult) {
String oldValue = this.intermediateResult;
String newValue = intermediateResult;
this.intermediateResult = intermediateResult;
firePropertyChange(INTERMEDIATE_RESULT, oldValue, newValue);
}
}
Which would look and run like:
If you want to cancel the jobs that have already started then you will have to use Callable instead of Runnable. When you submit a job you get back a Future which you can call cancel() on.
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
Callable<Integer> callable1 = new CallableImpl();
Future<Integer> future1 = es.submit(callable1);
// if you decide to cancel your task gracefully
future1.cancel()
...
It is then up to you to handle the ThreadInterrupt in your implementation of the Callable.
public class CallableImpl implements Callable<Integer> {
#Override
public Integer call() throws Exception {
try {
while(true) {
// do something
if(Thread.currentThread().isInterrupted()) {
System.out.println("detected interrupt flag");
break;
}
}
}
catch(InterruptedException ie) {
System.out.println("interrupted");
}
#Hovercraft is probably right, if you are coding a Swing app then SwingWorker is what you want to use.
If you are using a a JButton, with your ExecutorService, then you should probably create a new thread and release the Event Dispatch Thread (EDT):
button.setActionListener(new Action() {
public void actionPerformed(ActionEvent e) {
button.setEnabled(false); // disable the button
new Thread(new Runnable() {
public void run() {
... your code ...
button.setEnabled(true);
}
}).start();
}
});
Like Hovercraft Full of Eels said, awaitTermination is a blocking operation.
And in the case of Swing, you are probably doing that in an Action (like my example), and you are blocking the EDT from doing various operation such as responding to the user input :)
Note: the ExecutorService has a nice invokeAll which will could prove a bit useful than using awaitTermination. This will also blocks, and you'll still need to do your stuff in another thread if that's required.
Related
I'm using multi threading with Java, I have a thread that will post a message into a queue, a blockingQueue which is thread safe, and I have another thread, implementing a GUI with swing.
Everytime I'm checking whether the queue is empty or not, if not, I poll the message and add it to DefaultListModel, but the problem the display is not updated.
I made sure that the message polled is not empty.
This is the code:
For the implementation of the JList
historyListModel = new DefaultListModel<String>();
historyList = new JList<String>(historyListModel);
historyList.setAutoscrolls(true);
JScrollPane historyScroll = new JScrollPane(historyList);
add(historyScroll, BorderLayout.CENTER);
Adding message to the queue
private BlockingQueue<String> messageRes = new LinkedBlockingQueue<>();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
String msgReceived = new String(packet.getData(), 0, packet.getLength());
messageRes.add(msgReceived);
The thread to update the display
private class update implements Runnable{
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
}
}
}
}
Then this thread is called in the GUI main window
new Thread(new update()).start();
In order to make some tests, I tried the following code and It worked
private class update implements Runnable{
#Override
public void run() {
while (true){
Thread.sleep(1000)
historyListModel.addElement("hello world);
}
}
}
The previous code allows me to update the display every 1 second.
I tried one more code in order to triangulate the error:
private class update implements Runnable{
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
historyListModel.addElement("hello world);
}
}
}
}
No display was updated with the previous code.
Could anyone propose any explanation to what is happening ?
Thank you.
Swing is not thread-safe and is single-threaded. This means you should not be updating the UI from outside of the context of the Event Dispatching Thread nor should you be executing long-running/blocking calls within its context.
In your case, a SwingWorker would probably be the best solution, as you can poll the queue for new messages and publish them to the UI in a safe manner.
See...
Concurrency in Swing
Worker Threads and SwingWorker
for more details.
One thing I did note during my testing was this...
#Override
public void run() {
while (true){
if (!messageRes.isEmpty()){
historyListModel.addElement(messageRes.poll());
}
}
}
was probably causing the UI to be overloaded, as poll will either return the next element or null if there are none. So it was a "wild loop". Instead, you should probably be using take which will wait till a new value is available.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingWorker;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JList<String> messageList;
private DefaultListModel<String> model;
public TestPane() {
model = new DefaultListModel<>();
messageList = new JList<>(model);
setLayout(new BorderLayout());
add(new JScrollPane(messageList));
Consumer consumer = new Consumer();
Producer producer = new Producer();
ConsumerWoker worker = new ConsumerWoker(consumer, model);
worker.execute();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
while (true) {
consumer.add(producer.next());
Thread.sleep(500);
}
} catch (InterruptedException ex) {
}
}
});
thread.start();
}
}
public class Producer {
private List<String> master = Arrays.asList(new String[]{
"Malachy Keller",
"Ruqayyah Galvan",
"Harris Nunez",
"Nojus Riggs",
"Joan Mercer",
"Lynda Solomon",
"Raiden Fitzpatrick",
"Sebastian Ahmed",
"Jo Short",
"Nabeel Howarth",
"Maira Garrett",
"Patrik Knights",
"Mimi Mcgill",
"Antonina Villanueva",
"Kenya Hyde",
"Aleksander Rigby",
"Hasan Gilmore",
"Jessica Mcculloch",
"Seth Black",
"Marjorie Brewer",
"Elliot Gay",
"Oluwatobiloba Bowman",
"Domonic Saunders",
"Braden Hale",
"Muneeb Rankin",
"Ruby Tapia",
"Iris Hines",
"Afsana Ponce",
"Beverly Soto",
"Presley Bloggs",
"Leopold Goddard",
"Missy Browne",
"Deniz Woodcock",
"Gwion Ferreira",
"Stanley Mccall",
"Jayda Christie",
"Nikhil Plummer",
"Stacy Crosby",
"Cally Henry",
"Lilliana Taylor",
"Dolcie Navarro",
"Merryn Reynolds",
"Annalise Boyce",
"Anaya Cisneros",
"Aimie Piper",
"Celine Pearson",
"Clayton Battle",
"Danielle Briggs",
"Maddison Couch",
"Jorden Keeling",
"Iylah Holmes",
"Bethaney Quintero",
"Dominique Brett",
"Rohit Benjamin",
"Edgar Rodgers",
"Petra Salgado",
"Myrtle Deleon",
"Letitia Sheridan",
"Wasim Chester",
"Leela Simpson",
"Aine Rojas",
"Ava Mclean",
"Jerry Caldwell",
"Fraser Prosser",
"Callum Vang",
"Yasmin Ochoa",
"Gaia Daly",
"Vanessa Mathews",
"Scarlett Brook",
"Rhiann Fox",
"Nansi Cote",
"Dwayne Rowley",
"Junior Lucas",
"Becky Rush",
"Lori Guthrie",
"Safa Reed",
"Merlin Cartwright",
"Misbah Trejo",
"Khaleesi Ellison",
"Lena Wood",
"Bluebell Coffey",
"Sherry Hutton",
"Abi Delacruz",
"Kwabena Bright",
"Anastazja Kumar",
"Bronwyn Huffman",
"Atif Burke",
"Arwen Kirby",
"Bobbie Noble",
"Blane Bauer",
"Zander Sparrow",
"Marius Wormald",
"Rajan Perez",
"Teejay Faulkner",
"Imaani Rodriquez",
"Safaa Middleton",
"Rafael Livingston",
"Oakley Swan",
"Samiya Kim",
"Glen Beasley"
});
private List<String> avaliableMessages;
public Producer() {
avaliableMessages = new ArrayList<>(100);
}
public String next() {
if (avaliableMessages.isEmpty()) {
avaliableMessages.addAll(master);
}
return avaliableMessages.remove(0);
}
}
public class Consumer {
private BlockingQueue<String> messages = new LinkedBlockingDeque<>();
public void add(String message) {
messages.add(message);
}
public String next() throws InterruptedException {
return messages.take();
}
}
public class ConsumerWoker extends SwingWorker<Void, String> {
private AtomicBoolean keepRunning = new AtomicBoolean(true);
private Consumer consumer;
private DefaultListModel model;
public ConsumerWoker(Consumer consumer, DefaultListModel model) {
this.consumer = consumer;
this.model = model;
}
public void stop() {
keepRunning.set(false);
}
public Consumer getConsumer() {
return consumer;
}
#Override
protected Void doInBackground() throws Exception {
while (keepRunning.get()) {
String message = getConsumer().next();
publish(message);
}
return null;
}
#Override
protected void process(List<String> chunks) {
for (String msg : chunks) {
model.addElement(msg);
}
}
}
}
I wanted to monitor the progress of my file getting copied from source to destination. I have used synchronized keyword but somehow it not working as i expect it to be, my logic might be wrong. I will be glad if you help me out.
Here is my Code.
public class Download extends javax.swing.JFrame {
int val=0;
private Timer t;
private ActionListener a;
/* Creates new form Download */
public Download() {
initComponents();
jProgressBar1.setValue(val);
a = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if (jProgressBar1.getValue() < val)
jProgressBar1.setValue(jProgressBar1.getValue()+1);
else
t.stop();
}
};
}
public synchronized void copy(String source,String url)
{
try {
val+=25;
t=new Timer(200,a);
t.start();
FileInputStream fs = new FileInputStream(source);
FileOutputStream os = new FileOutputStream(url);
int b;
while ((b = fs.read()) != -1) {
os.write(b);
}
os.close();
fs.close();
} catch (Exception E) {
E.printStackTrace();
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
String url = null;
int returnValue = chooser.showDialog(null, "Select");
if (returnValue == JFileChooser.APPROVE_OPTION) {
url = chooser.getSelectedFile().getPath();
} else {
dispose();
}
JOptionPane.showMessageDialog(this,"Wait for Completion");
if(CB1.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Ele.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\\"+CB1.getText()+".pdf"));
}
if(CB2.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Mech.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\\"+CB2.getText()+".pdf"));
}
if(CB3.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Phy.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\\"+CB3.getText()+".pdf"));
}
if(CB4.isSelected()==true)
{
File f = new File(getClass().getResource("/PCycle/Civil.pdf").getFile());
String source= f.getAbsolutePath();
copy(source,(url+"\\"+CB4.getText()+".pdf"));
}
JOptionPane.showMessageDialog(this,"Completed");
try {
jProgressBar1.setValue(100);
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(Download.class.getName()).log(Level.SEVERE, null, ex);
}
System.exit(0);
}
}
Here I tried to implement a logic in such a that, whenever we call "copy" method it will copy the file from one location to another and before that it should run the timer method by which the progress on the jProgressBar is displayed. But unfortunately even after using synchronized it is not displaying the progress for each file.
The problem is you are blocking Swing's Event Dispatching Thread (EDT).
Swing does all drawing when the EDT is not busy responding to events. In this case jButton1ActionPerformed is not returning until all files have been copied. So although a Timer is started during each copy() call, the timers never get a chance to expire, because jButton1ActionPerformed has never returned.
In this case, you want to use a SwingWorker to copy the files in a background thread.
When you want to start copying the files:
start the timer in the main thread
create and start the SwingWorker.
open a model dialog to block further user actions (or otherwise disable the UI)
As the timer expires, your progress bar will advance, and be drawn.
When the SwingWorker is done() (which is executed on the EDT),
stop the timer
dismiss the dialog (or re-enable the UI)
Note: Do not create or access any UI items, or create/start/stop timers, from the background worker thread. These actions must only be performed on the EDT.
Rough example, showing disabling UI element, starting SwingWorker, publishing from the worker to show progress (which file is being download), enabling UI when the worker finishes.
File copy is faked using a 3 seconds sleep.
package progress;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Download extends JFrame {
public static void main(String[] args) {
SwingUtilities.invokeLater(Download::new);
}
private final JButton downloadBtn = new JButton("Start Download");
private final JProgressBar progressBar = new JProgressBar();
private final Timer timer = new Timer(200, this::timerTick);
Download() {
super("Download Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationByPlatform(true);
downloadBtn.addActionListener(this::startDownload);
add(downloadBtn, BorderLayout.PAGE_START);
progressBar.setStringPainted(true);
add(progressBar, BorderLayout.PAGE_END);
setVisible(true);
}
private void startDownload(ActionEvent evt) {
downloadBtn.setEnabled(false);
timer.start();
DownloadWorker worker = new DownloadWorker("File1", "FileB", "AnotherFile");
worker.execute();
}
private void timerTick(ActionEvent evt) {
progressBar.setValue(progressBar.getValue()+2);
}
private class DownloadWorker extends SwingWorker<Void, String> {
private final String[] files;
DownloadWorker(String ...files) {
this.files = files;
progressBar.setValue(0);
}
#Override
protected Void doInBackground() throws Exception {
for(String file : files) {
publish(file);
// Copy the file
Thread.sleep(3000); // Pretend copy takes a few seconds
}
return null;
}
#Override
protected void process(List<String> chunks) {
String file = chunks.get(chunks.size()-1); // Just last published filename
progressBar.setString("Downloading "+file + " ...");
}
#Override
protected void done() {
progressBar.setString("Complete");
progressBar.setValue(100);
timer.stop();
downloadBtn.setEnabled(true); // Re-enable UI
}
}
}
Below is the compiled program replica of actual problem code,
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class Dummy {
public static boolean getUserCheck(int size, boolean Check) {
if (Check) {
int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
"Warning", 0);
if (ret > 0) {
System.out.println("User said No: " + ret);
return false;
} else if (ret <= 0) {
System.out.println("user said Yes: " + ret);
return true;
}
}
return true;
}
public static void workerMethod1() {
System.out.println("am worker method 1");
}
public static void workerMethod2() {
System.out.println("am worker method 2");
}
public static void main(String[] args) {
System.out.println("mainthread code line 1");
int size = 13;
boolean thresholdBreach = true;
if (getUserCheck(size, thresholdBreach)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
workerMethod1();
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
workerMethod2();
}
});
}
System.out.println("mainthread code line 2");
System.out.println("mainthread code line 3");
}
}
where i would like to run the if{} block in main() on separate thread. Because these 2 lines,
System.out.println("mainthread code line 2");
System.out.println("mainthread code line 3");
need not wait for completion of if(){} block
Another problem is, experts recommend to run confirm-dialog methods on event thread.
int ret = JOptionPane.showConfirmDialog(null, size + " entries, Yes or no?",
"Warning", 0);
Please help me!!!!
JOptionPane is a Swing method and should be called on the EDT, the Event Dispatch Thread, and only on this thread, and so it suggests that all your code above should be on the EDT, and that most of your SwingUtilities.invokeLater(new Runnable() calls are completely unnecessary. The only necessary ones will be the main one, where you launch your Swing GUI code, and any areas where Swing calls need to be made from within background threads. Again, if any of the above code is being made within background threads, then the JOptionPane should not be in that thread.
For more specific information in this or any other answer, please provide more specific information in your question. Let's end all confusion. The best way to get us to fully and quickly understand your problem would be if you were to to create and post a minimal example program, a small but complete program that only has necessary code to demonstrate your problem, that we can copy, paste, compile and run without modification.
I have a sneaking suspicion that a decent refactoring along MVC lines could solve most of your problems. Your code is very linear with its lines of code that must follow one another and its if blocks, and it is also tightly coupled with your GUI, two red flags for me. Perhaps better would be less linear code, more event and state-driven code, code where your background code interacts with the GUI via observer notification, and where the background code likewise responds to state changes in the GUI from control notification.
Your control needs two SwingWorkers, one to get the row count and the other to get the rest of the data if the user decides to do so. I'd add a PropertyChangeListener to the first SwingWorker to be notified when the row count data is ready, and then once it is, present it to the view for the user to select whether or not to proceed. If he decides to proceed, I'd then call the 2nd SwingWorker to get the main body of the data.
For example, a rough sketch of what I'm talking about:
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class SwingWorkerFooView extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = 300;
private JProgressBar progressBar;
private JDialog dialog;
public SwingWorkerFooView() {
add(new JButton(new ButtonAction("Foo", this)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public boolean showOptionGetAllData(int numberOfRows) {
String message = "Number of rows = " + numberOfRows + ". Get all of the data?";
String title = "Get All Of Data?";
int optionType = JOptionPane.YES_NO_OPTION;
int result = JOptionPane.showConfirmDialog(this, message, title, optionType);
return result == JOptionPane.YES_OPTION;
}
public void showProgressBarDialog() {
progressBar = new JProgressBar();
progressBar.setIndeterminate(true);
Window window = SwingUtilities.getWindowAncestor(this);
dialog = new JDialog(window, "Hang on", ModalityType.APPLICATION_MODAL);
JPanel panel = new JPanel();
panel.add(progressBar);
dialog.add(panel);
dialog.pack();
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
}
public void closeProgressBarDialog() {
dialog.dispose();
}
private static void createAndShowGui() {
JFrame frame = new JFrame("SwingWorkerFoo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SwingWorkerFooView());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class ButtonAction extends AbstractAction {
Workers workers = new Workers();
private SwingWorker<Integer, Void> firstWorker;
private SwingWorker<List<String>, Void> secondWorker;
private SwingWorkerFooView mainGui;
public ButtonAction(String name, SwingWorkerFooView mainGui) {
super(name);
this.mainGui = mainGui;
}
#Override
public void actionPerformed(ActionEvent e) {
firstWorker = workers.createFirstWorker();
firstWorker.addPropertyChangeListener(new FirstPropertyChangeListener());
firstWorker.execute();
mainGui.showProgressBarDialog();
}
private class FirstPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
mainGui.closeProgressBarDialog();
try {
int numberOfRows = firstWorker.get();
boolean getAllData = mainGui.showOptionGetAllData(numberOfRows);
if (getAllData) {
secondWorker = workers.createSecondWorker();
secondWorker.addPropertyChangeListener(new SecondPropertyChangeListener());
secondWorker.execute();
mainGui.showProgressBarDialog();
} else {
// user decided not to get all data
workers.cleanUp();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
private class SecondPropertyChangeListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
mainGui.closeProgressBarDialog();
try {
List<String> finalData = secondWorker.get();
// display finalData in the GUI
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
class Workers {
// database object that may be shared by two SwingWorkers
private Object someDataBaseVariable;
private Random random = new Random(); // just for simulation purposes
private class FirstWorker extends SwingWorker<Integer, Void> {
#Override
protected Integer doInBackground() throws Exception {
// The Thread.sleep(...) is not going to be in final production code
// it's just to simulate a long running task
Thread.sleep(4000);
// here we create our database object and check how many rows there are
int rows = random.nextInt(10 + 10); // this is just for demonstration purposes only
// here we create any objects that must be shared by both SwingWorkers
// and they will be saved in a field of Workers
someDataBaseVariable = "Fubar";
return rows;
}
}
private class SecondWorker extends SwingWorker<List<String>, Void> {
#Override
protected List<String> doInBackground() throws Exception {
// The Thread.sleep(...) is not going to be in final production code
// it's just to simulate a long running task
Thread.sleep(4000);
List<String> myList = new ArrayList<>();
// here we go through the database filling the myList collection
return myList;
}
}
public SwingWorker<Integer, Void> createFirstWorker() {
return new FirstWorker();
}
public void cleanUp() {
// TODO clean up any resources and database stuff that will not be used.
}
public SwingWorker<List<String>, Void> createSecondWorker() {
return new SecondWorker();
}
}
The key to all of this is to not to think in a linear console program way but rather to use observer design pattern, i.e., listeners of some sort to check for change of state of both the GUI and the model.
It's essentially:
create worker
add observer to worker (property change listener)
execute worker
show progress bar dialog or notify user in some way that worker is executing.
The listener will be notified when the worker is done, and then you can query the worker (here via the get() method call) as to its end result.
Then the progress dialog can be closed
And the view can display the result or get additional information from the user.
Yes; SwingUtilities.invokeLater() simply places your runnable on the AWT event queue to be processed later, and it is safe to do so at any time.
I am generating SwingWorkers based on a number of connections I need to make. I am trying to make it so that I set a fixed number of maximum concurrant SwingWorkers and when one of those finishes another one is started (or many others are started if many have finished). Based on http://java.dzone.com/articles/multi-threading-java-swing I am setting up the basic SwingWorker like this:
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
System.out.println("One SwingWorker just ran! ");
}
return true;
}
// Can safely update the GUI from this method.
protected void done() {
boolean status;
try {
// Retrieve the return value of doInBackground.
status = get();
statusLabel.setText("Completed with status: " + status);
} catch (InterruptedException e) {
// This is thrown if the thread's interrupted.
} catch (ExecutionException e) {
// This is thrown if we throw an exception
// from doInBackground.
}
}
};
worker.execute();
Now I'm uncertain in how to implement the mechanism I described above.
From https://stackoverflow.com/a/8356896/988591 I saw that I can use an ExecutorService to execute instances of SwingWorker and that this interface also allows to set the number of threads:
int n = 20; // Maximum number of threads
ExecutorService threadPool = Executors.newFixedThreadPool(n);
SwingWorker w; //don*t forget to initialize
threadPool.submit(w);
I think this is what I need but I don't know how to put the whole thing together (..I am also quite new to Java..). Could someone guide me a bit in the process of implementing this? Say at the top I have int totalTask = 100; Maybe it's just a matter of some loops but I can't seem to find any really easy-to-follow examples around and I just can't totally wrap my mind around it yet so.. I would appreciate some help! Thanks.
UPDATE: I have set up the ExecutorService this way:
ExecutorService executorService = Executors.newFixedThreadPool(500);
for (int i = 0; i < 20 ; i++) {
executorService.submit(worker);
//I tried both...
//executorService.execute(worker);
}
and I have removed worker.execute() called after the SwingWorker above but the output from console is just a single "One SwingWorker just ran!" line, how is that ? What did I do wrong?
You'd do something like this:
Initiate the executorservice using a fixed threadPool as you have
shown.
In a loop create your runnable. As many runnables as you need.
You can have 50 threads and 5000 runnables. After the 1st 50
runnables, whichever thread is free will pick up the 51st task, and
so on.
Call the executorservice's execute method with your Runnable.
Once all are done, you shutdown the executor service.
Like this:
ExecutorService executorService = Executors.newFixedThreadPool(500);
for (long i = 0; i < 1000000; i++) {
Runnable populator = new YourRunnable();
executorService.execute(populator);
}
executorService.shutdown();
while(!executorService.isTerminated()){
}
That isTerminated can be used to check whether the executorServices is actually down. Since you can have several executor threads running even after you call the shutdown() method (because they haven't completed the task yet), that while loop acts like a wait call.
And one key thing: whatever you want to pass to the ExecutorService, it must be a Runnable implementation. In your case, your SwingWorker must be a Runnable.
That's interesting. With an ExecutorService with a limited pool, you just need to submit the workers whenever you want and only that amount of workers will be executed concurrently at the same time.
I made this little test app that where you can press the buttons to submit some workers, as fast as you want, and you can see how the amount of workers executing at any given time is never higher than the value numberOfThreads that you initialized the ExecutorService with.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class SwingWorkerExecutorTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new SwingWorkerExecutorTest();
}
});
}
public SwingWorkerExecutorTest()
{
JFrame frame = new JFrame("Frame");
int numberOfThreads = 2; //1 so they are executed one after the other.
final ExecutorService threadPool = Executors.newFixedThreadPool(numberOfThreads);
JButton button1 = new JButton("Submit SwingWorker 1");
button1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
String workerName = "Worker 1";
appendMessage("Submited " + workerName);
SwingWorker worker = new TestWorker(workerName);
threadPool.submit(worker);
}
});
JButton button2 = new JButton("Submit SwingWorker 2");
button2.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
String workerName = "Worker 2";
appendMessage("Submited " + workerName);
SwingWorker worker = new TestWorker(workerName);
threadPool.submit(worker);
}
});
JButton button3 = new JButton("Submit SwingWorker 3");
button3.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
String workerName = "Worker 3";
appendMessage("Submited " + workerName);
SwingWorker worker = new TestWorker(workerName);
threadPool.submit(worker);
}
});
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(button1);
buttonsPanel.add(button2);
buttonsPanel.add(button3);
frame.add(buttonsPanel, BorderLayout.PAGE_END);
_textArea = new JTextArea("Submit some workers:\n");
_textArea.setEditable(false);
frame.add(new JScrollPane(_textArea));
frame.setSize(600, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private class TestWorker extends SwingWorker
{
public TestWorker(String name)
{
_name = name;
}
#Override
protected Object doInBackground() throws Exception
{
String message = "A " + _name + " has started!";
appendMessage(message);
doHardWork();
return null;
}
#Override
protected void done()
{
String message = "A " + _name + " has finished!";
appendMessage(message);
}
private void doHardWork()
{
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
private String _name;
}
private static void appendMessage(String message)
{
_textArea.append(message + "\n");
System.out.println(message);
}
private static JTextArea _textArea;
}
It looks like this:
For example, with a number of threads of 2 you'll se how if you submit a lot of workers it takes 2 at a time and executes them.
Please take a look at the source code of SwingWorker.
You can something similar to execute() method
public final void execute() {
getWorkersExecutorService().execute(this);
}
At this point you can create your one ExecutorService and manage the pool
SwingWorker<Boolean, Void> test = new SwingWorker<Boolean, Void>() {
private ExecutorService service = new ThreadPoolExecutor(5, 10,
10L, TimeUnit.MINUTES,
new LinkedBlockingQueue<Runnable>(),
new ThreadFactory() {
AtomicInteger count= new AtomicInteger();
#Override
public Thread newThread(Runnable r) {
return new Thread("Pooled SwingWorker " + count.getAndAdd(1));
}
});
#Override
protected Boolean doInBackground() throws Exception {
return true;
}
public void doIt() {
service.execute(this);
}
};
That moment when you think: It was so obvious!
ExecutorService executorService = Executors.newFixedThreadPool(20);
for (int i = 0; i < 500; i++) {
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
#Override
protected Boolean doInBackground() throws Exception {
System.out.println("One SwingWorker just ran!");
return true;
}
protected void done() {
boolean status;
try {
status = get();
} catch (InterruptedException e) {
// This is thrown if the thread's interrupted.
} catch (ExecutionException e) {
// This is thrown if we throw an exception
// from doInBackground.
}
}
};
executorService.submit(worker);
}
It works great!
ive done some extensive searching on using threads in a loop and whilst I understand the concept how how seperate threads work, I still cant seem to grasp how to implement it in my simple application.
My application consists of a form with a text box. This textbox needs to be updated once ever iteration of a loop. It starts with the press of a button but the loop should also finish with the press of a stop button. Ive used a boolean value to track if its been pressed.
Here is my form code:
package threadtester;
public class MainForm extends javax.swing.JFrame {
public MainForm() {
initComponents();
}
private void RunButtonActionPerformed(java.awt.event.ActionEvent evt) {
ThreadTester.setRunnable(true);
ThreadTester example = new ThreadTester(2,this);
example.run();
}
private void StopButtonActionPerformed(java.awt.event.ActionEvent evt) {
ThreadTester.setRunnable(false);
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new MainForm().setVisible(true);
}
});
}
public void setTextBox(String myString){
MainTextbox.setText(myString);
}
}
As you can see I have a button that is pressed. When the button is pressed this executes the code thats in a different class called ThreadTester. Here is the code for that class:
package threadtester;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ThreadTester implements Runnable
{
int thisThread;
MainForm myMainForm;
private static boolean runnable;
// constructor
public ThreadTester (int number,MainForm mainForm)
{
thisThread = number;
myMainForm = mainForm;
}
public void run ()
{
for (int i =0;i< 20; i++) {
if(runnable==false){
break;
}
System.out.println("I'm in thread " + thisThread + " line " + i);
myMainForm.setTextBox(i + "counter");
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(ThreadTester.class.getName()).log(Level.SEVERE, null, ex);
}
} }
public static void setRunnable(Boolean myValue){
runnable = myValue;
}
public static void main(String[] args) {
MainForm.main(args);
}
}
as you can see the loop has been created on a seperate thread... but the textbox only updates after the loop has finished. Now as far as im aware in my MainForm I created a seperate thread to run the loop on, so I dont understand why its not running? Any guidence would be much appreciated, ive tried looking at examples on stack exchange but I cant seem to get them to fit into my implemntation.
With the recommendation suggested by Tassos my run method now looks like this:
public void run ()
{
for (int i =0;i< 20; i++) {
if(runnable==false){
break;
}
System.out.println("I'm in thread " + thisThread + " line " + i);
final String var = i + "counter";
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
myMainForm.setTextBox(var);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Logger.getLogger(ThreadTester.class.getName()).log(Level.SEVERE, null, ex);
}
} }
In order for Tassos' answer to work, you actually have to create an new thread, which you did not do. Simply calling
ThreadTester example = new ThreadTester(2,this);
example.run();
is not enough, sice that just calls the run method from EDT. You need to do the following:
Thread t = new Thread(new ThreadTester(2,this));
t.start();
Please refer to Defining and Starting a Thread.
Also, you want modify the same field from two different threads (runnable), which is a bug. You should read more about java concurrency.
Change this line
myMainForm.setTextBox(i + "counter");
into
final String var = i + "counter";
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
myMainForm.setTextBox(var);
}
});
}
Why? Because you can't do UI work in non-UI threads.
The problem is that you are blocking the EDT (Event Dispatching Thread), preventing the UI to refresh until your loop is finished.
The solutions to these issues is always the same, use a Swing Timer or use a SwingWorker.
Here is an example of the usage of a SwingWorker:
import java.awt.BorderLayout;
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.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class TestSwingWorker {
private JTextField progressTextField;
protected void initUI() {
final JFrame frame = new JFrame();
frame.setTitle(TestSwingWorker.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Clik me to start work");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
doWork();
}
});
progressTextField = new JTextField(25);
progressTextField.setEditable(false);
frame.add(progressTextField, BorderLayout.NORTH);
frame.add(button, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
protected void doWork() {
SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() {
#Override
protected Void doInBackground() throws Exception {
// Here not in the EDT
for (int i = 0; i < 100; i++) {
// Simulates work
Thread.sleep(10);
publish(i); // published values are passed to the #process(List) method
}
return null;
}
#Override
protected void process(List<Integer> chunks) {
// chunks are values retrieved from #publish()
// Here we are on the EDT and can safely update the UI
progressTextField.setText(chunks.get(chunks.size() - 1).toString());
}
#Override
protected void done() {
// Invoked when the SwingWorker has finished
// We are on the EDT, we can safely update the UI
progressTextField.setText("Done");
}
};
worker.execute();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestSwingWorker().initUI();
}
});
}
}