Why is my GUI unresponsive while a SwingWorker thread runs? - java

I have a SwingWorker thread with an IOBound task which is totally locking up the interface while it runs. Swapping out the normal workload for a counter loop has the same result. The SwingWorker looks basically like this:
public class BackupWorker extends SwingWorker<String, String> {
private static String uname = null;
private static String pass = null;
private static String filename = null;
static String status = null;
BackupWorker (String uname, String pass, String filename) {
this.uname = uname;
this.pass = pass;
this.filename = filename;
}
#Override
protected String doInBackground() throws Exception {
BackupObject bak = newBackupObject(uname,pass,filename);
return "Done!";
}
}
The code that kicks it off lives in a class that extends JFrame:
public void actionPerformed(ActionEvent event) {
String cmd = event.getActionCommand();
if (BACKUP.equals(cmd)) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final StatusFrame statusFrame = new StatusFrame();
statusFrame.setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run () {
statusFrame.beginBackup(uname,pass,filename);
}
});
}
});
}
}
Here's the interesting part of StatusFrame:
public void beginBackup(final String uname, final String pass, final String filename) {
worker = new BackupWorker(uname, pass, filename);
worker.execute();
try {
System.out.println(worker.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
So far as I can see, everything "long-running" is handled by the worker, and everything that touches the GUI on the EDT. Have I tangled things up somewhere, or am I expecting too much of SwingWorker?

I think the problem is due to the call to SwingWorker.get() in your beginBackup method. Take a look at the docs for this method:
Waits if necessary for the computation
to complete, and then retrieves its
result.
This is a blocking call, hence your GUI becomes unresponsive.
(Also, is there any particular reason why you're doing an invokeLater from within an invokeLater call? You're already running on the EDT.)

Read the section from the Swing tutorial on Tasks That Have Interim Results for a working example. You will see that the get(...) method is invoked from within the process(...) method that is overridden in the SwingWorker class.

Related

Java CountDownLatch wait for async callback

First of all, I'm a complete newbie in Java. What I want to achieve is wait for async results before I return them.
MainActivity.java:
...
private SendTransport.Listener sendTransportListener = new SendTransport.Listener() {
#Override
public String onProduce(JSONObject data) {
String result = receiveResult(data);
return result;
}
};
private String receiveResult(JSONObject data) throws InterruptedException {
String result = "";
final CountDownLatch latch = new CountDownLatch(1);
socket.emit("receive-result", data, new Ack() {
#Override
public void call(Object... args) {
result = args[1];
latch.countDown();
}
});
latch.await();
return result;
}
...
In the code above, the socket.emit function never executes. What's wrong with my code? Or better to use some other approach here? If yes, then what?

React-Native NativeModules - Is there an Android/Java callback pattern

In my react-native JS code I'm calling a Native Module and it was blocking the UI thread for about 1.5sec. Running it on a different thread with a Runnable works but I can't capture the returned value that happens inside the Runnable?
#ReactMethod
public void print(final String printerAddress, final String price, final String description, final String upc, Promise promise) {
try {
boolean success = false;
new Thread(new Runnable() {
public void run() {
success = mEpsonPrinter.printLabel(printerAddress, price, description, upc);
}
}).start();
promise.resolve(success);
} catch (IllegalViewOperationException e) {
promise.reject(e);
}
}
To resolve the immediate problem I placed the promise.resolve(success) call inside the Runnable.run()
try {
new Thread(new Runnable() {
public void run() {
boolean success = mEpsonPrinter.printLabel(printerAddress, price, description, upc);
promise.resolve(success);
}
}).start();
} catch (IllegalViewOperationException e) {
promise.reject(e);
}
Although I'm still left with questioning a solution for callback pattern with Java.

How to pause, resume & stop a "Process" & "dependent thread" run within SwingWorker

I am working on Applets where I am building a GUI like this:
I have used SwingWorkerThread so as to efficiently print 1000s of lines within few seconds on JTable. Actually from doInBackground() I am firing a command to capture logs, this is achieved using Process instance. Further there is a separate Thread that processes the input captured by Process instance.
But the problem is I am not able to implement functionality of "Pause" & "Stop" buttons.
I have seen some posts where functionality like this has been implemented:
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
if (!isPaused()) {
publish("Writing...");
} else {
Thread.sleep(200);
}
}
return null;
}
Problem 1:
But difficulty in my code is process.waitFor() is executed only once, I cant check "paused" variable or isCancelled() method in loop.
Also for the purpose of faster implementation, I have called the publish() method from different Threads (InputConsumer).
I am wondering how it is possible to pause the execution of both (Process & InputStream) threads simultaneously.
Problem 2: Implementation of "Stop" button.
The invocation of worker.cancel(true) doesn't stop the execution of threads created from doInBackground() method.
The working code with implementation of only "Run" button looks like this:
public class LogViewer extends javax.swing.JApplet {
BufferedReader br;
DefaultTableModel model;
TableSwingWorker worker;
#Override
public void init() {
try {
//initialize GUI
viewLogs();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void viewLogs() throws IOException{
model = (DefaultTableModel)jTable1.getModel();
worker = new TableSwingWorker(model);
}
public class TableSwingWorker extends SwingWorker<Integer, Object[]>{
private DefaultTableModel tableModel;
public TableSwingWorker(DefaultTableModel tableModel){
this.tableModel = tableModel;
}
#Override
protected Integer doInBackground() throws Exception {
String[] command = {"CMD", "/C", "adb logcat"};
ProcessBuilder probuilder = new ProcessBuilder(command);
Process process = probuilder.start();
InputConsumer consumer = new InputConsumer(process.getInputStream());
consumer.start();
int result = process.waitFor();
consumer.join();
return result;
}
#Override
protected void process(List<Object[]> chunks) {
for(Object[] row: chunks)
tableModel.addRow(row);
}
protected void processOutput(String text){
//process text received
publish(new Object[]{line, date, time, PID, TID, loglevel, tag, message});
}
public class InputConsumer extends Thread {
private InputStream is;
public InputConsumer(InputStream is){
this.is = is;
start();
}
#Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
try{
String text = null;
while((text = br.readLine()) != null){
processOutput(text);
}
}
catch(IOException ex){
ex.printStackTrace();
}
}
}
}
}
Any help on the issue will be deeply appreciated. Kindly comment for more clarification from my side.

Java ThreadPool reporting

I have a worker threadpool set up that executes a bit of work which I want to log in a central place.
To be more precise, I've extended the Thread class into a worker class, which checks the status of a concurrent queue. If it's empty, then it waits. As elements are added by another thread, notify() wakes the workers. Once they've completed the task, they wait for the next element in the queue.
What's the best practice to have each of the threads report their status at the end of each of their tasks?
public class PoolWorker extends Thread {
public ConcurrentLinkedQueue<Device> q;
public PoolWorker(ConcurrentLinkedQueue<Device> q, String type){
this.q = q;
this.type = type;
}
#Override
public void run(){
while (true)
{
Device d = null;
try{
synchronized(q){
while(q.isEmpty())
{
q.wait(); // wait for a notify()
}
d = q.remove();
}
// do some work
// report status of work completed
}
}
Try to do something like this
ExecutorService exec = Executors.newFixedThreadPool(10);
Runnable runn = new Runnable()
{
#Override
public void run()
{
System.out.println("");
}
};
exec.execute(runn);
As mentioned best way is to use BlockingQueue. Below is the sample code:
public class PoolWorker extends Thread {
public ArrayBlockingQueue<String> q;
public String type;
public PoolWorker(ArrayBlockingQueue<String> q, String type) {
this.q = q;
this.type = type;
}
#Override
public void run() {
while(true){
String work = null;
try {
System.out.println("PoolWorker.run:waiting .............");
work = q.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("PoolWorker.run..work: " + work);
}
}
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<String> pool = new ArrayBlockingQueue<String>(100);
PoolWorker worker = new PoolWorker(pool, "Something");
worker.start();
addWork(pool, "work1");
addWork(pool, "work2");
addWork(pool, "work3");
addWork(pool, "work4");
addWork(pool, "work5");
//Just give enough time to run
Thread.sleep(5000);
}
private static void addWork(ArrayBlockingQueue<String> pool, String work) throws InterruptedException {
System.out.println("PoolWorker.addWork: " + work);
pool.put(work);
}
}
There is nice sample code available in Java documentation as well:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

how to restart a thread

I tried to write a file monitor which will check the file if a new line is appended,the monitor in fact is a thread which will read the line by a randomaccessfile all the time.
This is the monitor core codes:
public class Monitor {
public static Logger log = Logger.getLogger(Monitor.class);
public static final Monitor instance = new Monitor();
private static final ArrayList<Listener> registers = new ArrayList<Listener>();
private Runnable task = new MonitorTask();
private Thread monitorThread = new Thread(task);
private boolean beStart = true;
private static RandomAccessFile raf = null;
private File monitoredFile = null;
private long lastPos;
public void register(File f, Listener listener) {
this.monitoredFile = f;
registers.add(listener);
monitorThread.start();
}
public void replaceFile(File newFileToBeMonitored) {
this.monitoredFile = newFileToBeMonitored;
// here,how to restart the monitorThread?
}
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
try {
if (raf != null) {
raf.close();
lastPos = 0;
}
raf = new RandomAccessFile(monitoredFile, "r");
log.info("monitor file " + monitoredFile.getAbsolutePath());
} catch (FileNotFoundException e) {
// The file must exist now
} catch (IOException e) {}
}
private void startRead() {
beStart = true;
String line;
while (beStart) {
try {
raf.seek(lastPos);
while ((line = raf.readLine()) != null) {
fireEvent(new FileEvent(monitoredFile.getAbsolutePath(),
line));
}
lastPos = raf.getFilePointer();
} catch (IOException e1) {}
}
}
private void stopRead() {
this.beStart = false;
}
private void fireEvent(FileEvent event) {
for (Listener lis : registers) {
lis.lineAppended(event);
}
}
private class MonitorTask implements Runnable {
#Override
public void run() {
stopRead();
//why putting the resetReandomAccessFile in this thread method is that it will sleep if the file not exist.
setRandomFile();
startRead();
}
}
}
This is some help classes:
public interface Listener {
void lineAppended(FileEvent event);
}
public class FileEvent {
private String line;
private String source;
public FileEvent(String filepath, String addedLine) {
this.line = addedLine;
this.source = filepath;
}
//getter and setter
}
And this is a example to call the monitor:
public class Client implements Listener {
private static File f = new File("D:/ab.txt");
public static void main(String[] args) {
Monitor.instance.register(f, new Client());
System.out.println(" I am done in the main method");
try {
Thread.sleep(5000);
Monitor.instance.replaceFile(new File("D:/new.txt"));
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
#Override
public void lineAppended(FileEvent event) {
String line = event.getLine();
if (line.length() <= 0)
return;
System.err.println("found in listener:" + line + ":" + line.length());
}
}
Now,my probelm is the code work well if I just call:
Monitor.instance.register(file,listener);
This will monitor the file for line appending,and will notify the listener.
However it does not work when I call the :
Monitor.instance.replaceFile(anotherfile);
This means I want to monitor another file rather than before.
So in my Monitor I have to restart the thread,how to make it?
I have tried the:
monitorThread.interruppt();
It does not wrok.
Anyone can fix it for me or tell me how to do ?
Thanks.
Before I ask,I have googling the "restart java thread",so I know one can not restart a dead thread,but my thread does not return,so I think it can be restarted.
You don't restart a Thread, instead you create a new one each time you want to start a thread.
A better alternative may be to use Executors.newCachedThreadPool() which gives you a pool of thread which will be started/recycle for you.
BTW: You are using recursion rather than a loop to poll if the file exists. Using recursion can mean if you wait too long it will throw a StackOverflowError. IMHO you shouldn't wait at all, the polling thread should repeatedly attempt to open the file until it is told to stop (or the file appears)
Your current implementation also means if the file is replaced, you will have to reopen the file in the background thread anyway.
Instead of explaining, I just coded up a skeleton example. I did not test it terribly well, but it may be of some use.
In order to monitor a(nother) file, just create a new Monitor, passing it a ScheduledExecutorService. Starting and stopping monitoring is straightforward. You can (should) reuse the same executor for multiple monitors.
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public interface Event
{
}
public interface Listener
{
void handle(Event event);
}
public class Monitor
{
private static final int CHECK_EVERY_SECONDS = 10;
private static final int RECHECK_AFTER_IF_NOT_EXISTS_SECONDS = 30;
private File file;
private ScheduledExecutorService executor;
private boolean active;
private List<Listener> listeners;
public Monitor(File file, ScheduledExecutorService executor)
{
super();
this.file = file;
this.executor = executor;
listeners = new ArrayList<Listener>();
}
public synchronized void start()
{
if (active)
{
return;
}
active = true;
executor.execute(new Runnable()
{
public void run()
{
synchronized (Monitor.this)
{
if (!active)
{
System.out.println("not active");
return;
}
}
if (!file.exists())
{
System.out.println("does not exist, rescheduled");
executor.schedule(this, RECHECK_AFTER_IF_NOT_EXISTS_SECONDS, TimeUnit.SECONDS);
return;
}
Event event = doStuff(file);
System.out.println("generated " + event);
updateListeners(event);
System.out.println("updated listeners and rescheduled");
executor.schedule(this, CHECK_EVERY_SECONDS, TimeUnit.SECONDS);
}
});
}
private Event doStuff(final File file)
{
return new Event()
{
public String toString()
{
return "event for " + file;
}
};
}
public synchronized void stop()
{
active = false;
}
public void addListener(Listener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
public void removeListener(Listener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
private void updateListeners(Event event)
{
synchronized (listeners)
{
for (Listener listener : listeners)
{
listener.handle(event);
}
}
}
public static void main(String[] args) throws IOException
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
File file = new File("test.png");
Monitor monitor = new Monitor(file, executor);
monitor.addListener(new Listener()
{
public void handle(Event event)
{
System.out.println("handling " + event);
}
});
monitor.start();
System.out.println("started...");
System.in.read();
monitor.stop();
System.out.println("done");
executor.shutdown();
}
}
See this post How to start/stop/restart a thread in Java?
I assume you answered your question
one can not restart a dead thread
This link may be helpful to you How to restart thread in java?
A thread in Java cannot be re-started. Every time you need to restart the thread you must make a new one.
That said, you might want to look at:
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
// ....
}
Here you sleep for 30 seconds if the file does not exist, then recursively call the same function. Now, I don't know what business requirements you have, but if this recursion ran long enough you will run out of stack space. Perhaps you will be better served with a while loop or even better, a little synchronisation like a Semaphore.

Categories

Resources