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);
}
}
}
}
Related
Here is my code... How can I make it work so that it runs the loop while the user is holding a button and stops when the user releases the button?
public void nextPrimeNum()
{
x = false;
int b = 2;
ArrayList<Integer> next = new ArrayList<Integer>();
while(x)
{
next = factors(b);
if(next.size()==2)
{
System.out.println(b);
}
b++;
}
System.out.println("End");
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == 401)
{
x = true;
}
}
public void keyRealesed(KeyEvent e)
{
if(e.getKeyCode() == 402)
{
x = false;
}
}
GUI and multi-thread programming is inherently difficult.
So, this is as simple as it could be, without violating best practices too much.
You need several things:
A separate Thread for printing primes:
Its run method loops for ever, but pauses when the Space key is not pressed.
(see Defining and Starting a Thread for more info)
A KeyListener which will be called from AWT's event dispatch thread:
The event handling methods are designed to finish fast, so that other events
(like moving, resizing and closing the frame) still are handled fast.
(see How to Write a Key Listener
and The Event Dispatch Thread for more info)
A visible GUI component (JFrame) for adding the KeyListener
Some synchronization between the 2 threads (via synchronized, notify and wait)
so that the prime-printing starts/continues on keyPressed
and suspends on keyReleased
(see Guarded Blocks for more info)
Initialize and start the whole GUI by invoking initGUI.
(see Initial Threads for more info)
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class Main implements Runnable, KeyListener {
public static void main(String[] args) {
SwingUtilities.invokeLater(Main::initGUI);
}
private static void initGUI() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JLabel("Press SPACE key for printing primes"));
frame.pack();
frame.setLocationRelativeTo(null); // center on screen
frame.setVisible(true);
Main main = new Main();
frame.addKeyListener(main);
Thread thread = new Thread(main);
thread.start();
}
private boolean spaceKeyPressed;
private boolean isPrime(int n) {
for (int i = 2; i < n; i++) {
if (n % i == 0)
return false;
}
return true;
}
#Override
public void run() {
for (int n = 2; /**/; n++) {
while (!spaceKeyPressed) {
synchronized (this) {
try {
wait(); // waits until notify()
} catch (InterruptedException e) {
// do nothing
}
}
}
if (isPrime(n)) {
System.out.println(n);
}
}
}
#Override
public void keyTyped(KeyEvent e) {
// do nothing
}
#Override
public synchronized void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = true;
notifyAll(); // cause wait() to finish
}
}
#Override
public synchronized void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
spaceKeyPressed = false;
notifyAll(); // cause wait() to finish
}
}
}
So, the answer is - it's complicated. It covers broad topics such as concurrency (in general), GUI development, best practices with the specific API (Swing) which are better covered in more detail by reading through the various tutorials (and experimenting)
Concurrency
Creating a GUI With JFC/Swing
Concurrency in Swing
Worker Threads and SwingWorker
How to Use Actions
How to Use Key Bindings
The example presents two ways to execute the "loop" (which is presented in the doInBackground method of the CalculateWorker class).
You can press and hold the JButton or press and hold the [kbd]Space[kbd] bar, both will cause the "main loop" to run, updating the JTextArea with the results...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea ta;
private CalculateWorker worker;
public TestPane() {
setLayout(new BorderLayout());
ta = new JTextArea(20, 20);
ta.setEditable(false);
add(new JScrollPane(ta));
worker = new CalculateWorker(ta);
JButton btn = new JButton("Press");
btn.getModel().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
System.out.println("...isRunning = " + worker.isRunning());
if (!worker.isRunning()) {
return;
}
System.out.println("...isPressed = " + btn.getModel().isPressed());
System.out.println("...isPaused = " + worker.isPaused());
if (btn.getModel().isPressed()) {
worker.pause(false);
} else {
worker.pause(true);
}
}
});
add(btn, BorderLayout.SOUTH);
worker.execute();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, false), "Space.released");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0, true), "Space.pressed");
am.put("Space.released", new CalculateAction(false, worker));
am.put("Space.pressed", new CalculateAction(true, worker));
}
public class CalculateWorker extends SwingWorker<List<String>, String> {
private AtomicBoolean run = new AtomicBoolean(true);
private AtomicBoolean paused = new AtomicBoolean(false);
private ReentrantLock pausedLocked = new ReentrantLock();
private Condition pausedCondition = pausedLocked.newCondition();
private JTextArea ta;
public CalculateWorker(JTextArea ta) {
this.ta = ta;
pause(true);
}
public void stop() {
run.set(false);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public void pause(boolean pause) {
paused.set(pause);
pausedLocked.lock();
pausedCondition.signalAll();
pausedLocked.unlock();
}
public boolean isPaused() {
return paused.get();
}
public boolean isRunning() {
return run.get();
}
#Override
protected List<String> doInBackground() throws Exception {
List<String> values = new ArrayList<>(256);
long value = 0;
System.out.println("!! Start running");
while (run.get()) {
while (paused.get()) {
System.out.println("!! I'm paused");
pausedLocked.lock();
try {
pausedCondition.await();
} finally {
pausedLocked.unlock();
}
}
System.out.println("!! Start loop");
while (!paused.get() && run.get()) {
value++;
values.add(Long.toString(value));
publish(Long.toString(value));
Thread.sleep(5);
}
System.out.println("!! Main loop over");
}
System.out.println("!! Run is over");
return values;
}
#Override
protected void process(List<String> chunks) {
for (String value : chunks) {
ta.append(value);
ta.append("\n");
}
ta.setCaretPosition(ta.getText().length());
}
}
public class CalculateAction extends AbstractAction {
private boolean start;
private CalculateWorker worker;
public CalculateAction(boolean start, CalculateWorker worker) {
putValue(NAME, "Calculate");
this.start = start;
this.worker = worker;
}
#Override
public void actionPerformed(ActionEvent e) {
worker.pause(start);
}
}
}
}
Is there a simpler solution?
Of course, I always go for the most difficult, hard to understand solutions first (sarcasm)
While it "might" be possible to reduce the complexity, the example presents a number of "best practice" concepts which you would do well to learn and understand.
The solution could also be done differently depending on the API used, so, it's the "simplest" solution for the specific API choice.
I wanted to do it from the console!
Java can't do that - it's console support is rudimentary at best and doesn't support a concept of "key pressed/released" actions (since it's running in a single thread, it would be impossible for it to do otherwise).
There "are" solutions you might try, but they would require a third party library linked to native binaries to implement, which would (possibly) reduce the number of platforms it would run on
I have problems with Java's Multi-Threading feature, so hopefully somebody can help me....
Here is my problem:
In the JPanel ExamplePanel which is located in the JFrame ExampleFrame I've added a ComponentListener which invokes the startPaint()-Method. This method should work in a new Thread. My Problem is that by resizing the window "former" Threads aren't closed, meanwhile new Threads are added....
So is there a way to resize the JPanel and to close at the same time the "old" threads, so that the number of threads is not growing, when I resize the JPanel?
I have tried something with a boolean exiter-variable, but it do not seemed to work...
here is the code:
package example;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Example2 {
public static void main(String[] args) throws InterruptedException {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new ExampleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class ExampleFrame extends JFrame {
private static final long serialVersionUID = 1L;
ExamplePanel examplePanel = new ExamplePanel();
private Thread t=null;
private class ExamplePanel extends JPanel implements ComponentListener {
private static final long serialVersionUID = 1L;
#Override
public void componentHidden(ComponentEvent e) {
}
#Override
public void componentMoved(ComponentEvent e) {
}
#Override
public void componentResized(ComponentEvent e) {
startPaint();
}
#Override
public void componentShown(ComponentEvent e) {
}
private void startPaint() {
t=new Thread(new Runnable() {
#Override
public void run() {
//System.out.println(Thread.currentThread().getName());
while (true) {
//System.out.println(Thread.activeCount());
}
}
});
t.start();
}
}
public ExampleFrame() {
examplePanel.addComponentListener((ComponentListener) examplePanel);
getContentPane().add(examplePanel);
}
}
if the calculations don't take long don't use an extra Thread.
if you need this extra Thread make sure that it doesn't run forever (no while (true) without returning at some point)
you can always interrupt your running Thread bfore creating the new one
if (t != null && t.isAlive()) {
t.interrupt();
}
and check in the while(true) loop if the Thread is interrupted
if (t.isInterrupted()) {
System.out.println("Thread ended");
return;
}
hope this helps
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.
public class ListExample {
public static void main(String[] args) {
final List l=new List();
l.init();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
l.list.updateUI();
System.out.println("Asim");
}
});
}
}
public class List extends JFrame{
public DefaultListModel DLM=new DefaultListModel();
public JList list=new JList(DLM);
private JPanel jp=new JPanel();
private JScrollPane sp=new JScrollPane(list);
public void init(){
jp.add(sp);
super.add(jp);
super.setVisible(true);
super.setLayout(new FlowLayout());
super.setSize(400,500);
UL p=new UL();
p.UL_1();
}
public void Update_list(String[] n){
list.setListData(n);
list.updateUI();
}
}
public class UL extends List{
public void UL_1(){
t.start();
}
Thread t=new Thread(new Runnable() {
#Override
public void run() {
//To change body of implemented methods use File | Settings | File Templates.
String[] n={"Asim", "saif","Khan"};
List l=new List();
l.list.setListData(n);
list.updateUI();
}
});
}
Swing is a single threaded framework, this means that your are expected to only modify the UI elements from within the context of the Event Dispatching Thread. Equally, any long run task or other blocking process will prevent the EDT from processing new events.
While there are a number of ways you might achieve this, probably the simplest would be to use a SwingWorker
For example...
Populate JList with threads
Take a look at Concurrency in Swing for more details
You'd better not to write your java code in that bad-style.
Don not make xxxList extends JFrame ,which will cause misunderstanding.
To use a thread update a list,may be you can start a new thread,like follwing:
Before: not encouraged to use another non EDT thread,incur error.
import java.awt.FlowLayout;
import javax.swing.*;
/*
* This code is bad dealing with Swing component update
*
* Update a Swing component from A Non-EDT thread is not encouraged
* may incur error like:
*
* Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
*/
public class ListExampleBad {
public static void main(String[] args) {
final ListFrame listFrame=new ListFrame();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//use a thread to update list data
new Thread(new UpdateList(listFrame)).start();
System.out.println("Asim");
}
});
}
}
//list fram with JList
class ListFrame extends JFrame{
public ListFrame(){
jp.add(sp);
super.add(jp);
super.setVisible(true);
super.setLayout(new FlowLayout());
super.setSize(400,500);
}
public synchronized void updateList(String[] n){
list.setListData(n);
list.updateUI();
}
private static final long serialVersionUID = 1L;
private DefaultListModel<String> DLM=new DefaultListModel<String>();
private JList<String> list=new JList<String>(DLM);
private JPanel jp=new JPanel();
private JScrollPane sp=new JScrollPane(list);
}
//runnable dealing with data update
class UpdateList implements Runnable {
public UpdateList(ListFrame listFrame) {
this.ListFrame = listFrame;
}
#Override
public void run() {
// TODO Auto-generated method stub
if(SwingUtilities.isEventDispatchThread()) {
System.out.println("updating list from Event Dispatch Thread");
}else {
System.out.println("updating list NOT from Event Dispatch Thread");
}
String[] n={"Asim", "saif","Khan"};
ListFrame.updateList(n);
}
private ListFrame ListFrame;
}
Thank you who make comments, I update the code:
《core java》 guides us tow principle:
1)don't do a time-consuming job in EDT thread,using a SwingWorker.
2)Except from EDT,don not operate Swing componnet in other thread.
Besides,you can use SwingUtilities.isEventDispatchThread() to check if the job is doing in a EDT thread.
one way: use SwingUtilities.invokeLater
public class ListExampleBetter {
public static void main(String[] args) {
final ListFrameBad listFrame=new ListFrameBad();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//use SwingUtilities.invokeLater,it's ok
SwingUtilities.invokeLater(new UpdateList(listFrame));
}
}
another way:using SwingWoker
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.*;
/**
* This example illustrate updating JList from thread
*/
public class ListExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ListFrame listFrame = new ListFrame();
listFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
listFrame.setLocationRelativeTo(null);
listFrame.setVisible(true);
}
});
}
}
//frame with JList
class ListFrame extends JFrame{
public ListFrame(){
super("Update JList Demo");
//initalize data field
dataToUpdate =new String[]{"Asim", "saif","Khan"};
DefaultListModel<String> DLM =new DefaultListModel<String>();
DLM.addElement("wait for update...");;
list =new JList<String>(DLM);
//build gui
JPanel btnPanel = new JPanel();
JButton btnUpdate = new JButton("Update");
btnPanel.add(btnUpdate);
JScrollPane sp=new JScrollPane(list);
this.add(btnPanel,BorderLayout.NORTH);
this.add(sp,BorderLayout.CENTER);
this.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);
//deal with action
btnUpdate.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
ListUpdater updater = new ListUpdater();
updater.execute();
}
});
}
public synchronized void updateList(String[] n){
list.setListData(n);
list.updateUI();
}
//using Swingworker to update list
private class ListUpdater extends SwingWorker<Void,String>{
#Override
public Void doInBackground() {
for(String str :dataToUpdate ) {
publish(str);
}
return null;
}
#Override
public void process (List<String> datas) {
for(String str : datas) {
model.addElement(str);
}
}
#Override
public void done() {
if(SwingUtilities.isEventDispatchThread()) {
System.out.println("updating list from Event Dispatch Thread");
}else {
System.out.println("updating list NOT from Event Dispatch Thread");
}
list.setModel(model);
}
private DefaultListModel<String> model =new DefaultListModel<String>();
}
public String[] getDataToUpdate() {
return dataToUpdate;
}
public void setDataToUpdate(String[] dataToUpdate) {
this.dataToUpdate = dataToUpdate;
}
private static final long serialVersionUID = 1L;
private final int DEFAULT_WIDTH = 300,DEFAULT_HEIGHT = 300;
private JList<String> list ;
private String[] dataToUpdate ;
}
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();
}
});
}
}