I have a Java app that processes a datastream from data arriving via a serial port, and it displays a summary in a Swing UI.
It works fine, but when I set breakpoints in Eclipse in certain threads (e.g. the Swing event dispatch thread), I have a limited amount of time before the JVM crawls to a halt: the incoming data is still being processed, and some system queue, whether it's a data queue or an event queue, is getting overfilled.
Is there any way I can detect this in upstream threads, so that my upstream processing starts throwing away data during debugging?
If my program explicitly uses a queue, I can just throw away data when the queue size gets too high.
But I can't do this if the queue is "implicit", e.g. it's managed by some other piece of software beyond my direct control. I can think of two possibilities:
If I'm using SwingUtilities.invokeLater(), or another UI framework that calls SwingUtilities.invokeLater(), how can I detect whether the Dispatch thread is backed up with events?
If I'm using ExecutorService.submit(), how can I detect whether the executor's task queue is backed up?
update: I think I've solved #2 by wrapping my ExecutorService:
AbstractPipelineExecutor.java:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
/**
* pipeline executor
*/
abstract public class AbstractPipelineExecutor {
/**
* a pipeline scheduled item
*/
public interface Task extends Runnable
{
/**
* if item cannot be run, this is called instead
*/
public void denied();
}
final private ExecutorService executor;
public AbstractPipelineExecutor(ExecutorService executor)
{
this.executor = executor;
}
/**
* submit an item to be executed
* @param task pipeline item
*/
public Future<?> submit(final Task task)
{
Future<?> result = null;
if (this.executor.isShutdown())
{
task.denied();
}
else
{
try
{
onSubmit(task);
result = this.executor.submit(new Runnable() {
@Override public void run()
{
onBeginExecute(task);
try
{
task.run();
}
catch (RuntimeException e)
{
onExecutionException(task, e);
}
finally
{
onEndExecute(task);
}
}
});
}
catch (RejectedExecutionException e)
{
task.denied();
}
}
return result;
}
/**
* event handler: item is submitted
* @param task pipeline item
*/
abstract protected void onSubmit(Task task) throws RejectedExecutionException;
/**
* event handler: item execution is begun
* @param task pipeline item
*/
protected void onBeginExecute(Task task) {}
/**
* event handler: item throws a runtime exception
* @param task pipeline item
*/
protected void onExecutionException(Task task, RuntimeException e) {
throw(e);
}
/**
* event handler: item execution is ended
* @param task pipeline item
*/
protected void onEndExecute(Task task) {}
}
BoundedPipelineExecutor.java:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
public class BoundedPipelineExecutor extends AbstractPipelineExecutor {
public BoundedPipelineExecutor(ExecutorService executor, int bound) {
super(executor);
this.q = new ArrayBlockingQueue<Task>(bound);
}
final private ArrayBlockingQueue<Task> q;
@Override public void onSubmit(Task task)
{
if (!this.q.offer(task))
throw new RejectedExecutionException(task.toString());
}
@Override public void onBeginExecute(Task task)
{
this.q.remove();
}
}
Rather than using SwingUtilities.invokeLater directly from your IO classes, I'd suggest that they should update a 'summariser' that can make informed decisions about updating the UI. Even without debug breakpoints, you don't want heavy IO operations to flood your user interface.
So, create a class that receives and processes your data, and allow your user interface to poll that information at suitable intervals.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With