The task I need to perform involves requesting some data from an external server, performing some (fairly lengthy) processing on the data, and then updating the GUI with the results of the processing. Because the server might be unresponsive, the task is well suited to a SwingWorker: the doInBackground() method gets the results, and then the done method updates the GUI.
I need this to happen once every few seconds. I know I can just use a while loop and Thread.sleep, and create a new SwingWorker after each sleep. But everything I've read frowns upon using loops and sleep. I'd like to use a timer but:
Using a swing timer seems counterproductive; since they run on the EDT, I would essentially have no reason to use SwingWorker's doInBackground method. If the server were not responsive, the GUI would be unresponsive.
Using a java.util.Timer seems a bit wasteful: it seems to create a background thread for the TimerTask(), and since I am just creating a SwingWorker to do the actual work, I'm essentially creating a background thread that creates another background thread.
Can anybody tell me what's the best solution? I'd like to stick with SwingWorker since it seems ideally suited to the task, but I would like to avoid using a while loop if I can help it.
Thanks
You could use a ScheduledExecutorService:
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
// get a scheduled executor service with one thread
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// schedule the worker at an intervall of 5 seconds
scheduler.scheduleAtFixedRate(myWorker, 0, 5, TimeUnit.SECONDS);
I don't see why you couldn't use a Swing Timer to start a Swing Worker. What have you tried?
I think you're on the right track with SwingWorker. Now you need to look at its publish and process methods. As your processing progresses, you publish() an object from the background thread then the process() method is called on the Swing(EDT) thread so you can update the gui.
This way there aren't a bunch of timers and other threads to coordinate.
The javadocs have a straightforward example with prime numbers:
http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html
How large is the set of data you are retrieving? If it is fairly small I would completely detach the task of fetching/processing and displaying.
Use some sort of in memory cache to hold the most recently processed set of data.
Use a javax.swing.Timer to update the GUI with the cached data.
Use a java.util.Timer to fetch the data from the database, process it and update the cache.
Be careful of synchronisation issues between the two times on your cache. You don't want your swing timer grabbing data at the same time as the other timer is updating it.
Related
I was wondering if there was a form of timer that I can use that doesn't pause all of my code Thread.sleep(ms) is not what I need because it pauses all of my code.
EDIT: Okay, I think I misworded that. Here's my edit:
Is there a way to measure a certain amount of time in Java without pausing my main method?
I like to using Executors class. It have a nice method that is newScheduledThreadPool. It gives you an ScheduledExecutorService instance, which have a lot of scheduling methods.
Check it out here here:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.schedule(threadToExecute, 10, TimeUnit.SECONDS);
This code will start threadToExecute thread after 10 seconds, without pausing your main thread.
Depends on which Frameworks you use. A pretty generic way would be to start a new Thread, pause that Thread via Thread.sleep(ms) and then do whatever you want to do after that delay, but of course that requires a little bit of care because of concurrency issues.
You might want to look into the Timer class. You can attach it to a thread, schedule events, and add delays. What you could do with this is you could create a thread to run along side your main thread and have it run from there. Because they will be on two completely different threads, you don't need to worry about them interfering with each other. To run the other thread, you could create a class that extends Thread and run it. Then use the Timer(String name) constructor to create a Timer for the thread.
I read probably every tutorial and every forum discussion on that subject but still can't make it happen! and it is very frustrating.
it seems that the way to do it is to use executeOnExecutor() method with - AsyncTask.THREAD_POOL_EXECUTOR, and so i did in my code. but still,the second task only beeing executed only after the first one has finished and not in the same time.
My min sdk version is 2.3 and the maximum is 4.2, so i did the check:
if (android.os.Build.VERSION.SDK_INT >=android.os.Build.VERSION_CODES.HONEYCOMB) {
engine.setEnginesTurn(true);
engineThread = new EngineThread(board,engine,activity,boardView);
rt = new RotateTask(boardView);
engineThread.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
rt.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else{
engine.setEnginesTurn(true);
engineThread = new EngineThread(board,engine,activity,boardView);
rt = new RotateTask(boardView);
engineThread.execute();
rt.execute();
}
Ignore the boolean variable here..it is not relevant,and also the other code since you wondered why i didn't post it. it is a mess but all working just fine,execpt for the execution of the two tasks. what am i doing wrong?
The reason i want the two tasks running parallely is: the first tasks is a computation task and the other one is a custom Hourglass image rotating animation while the computer is thinking (Its a game app).
EDIT: Ah.. and just wanted to include that i don't do the animation on the main UI thread is because i use sleep() for the animation ,so can't freeze the main thread.
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
So for parallel execution you can't use asynctask. The above is quoted from the doc. So for parallel execution i suggest you look at executor.
http://developer.android.com/reference/java/util/concurrent/Executor.html
It sounds like you are modifying the UI from the background thread in your AsyncTask. This is not thread safe and is probably causing the problem.
Keep your computation task on a separate thread and move your animation back onto the UI thread and (unless I am missing something) that should do the trick. Remember that anything that is drawn to the screen must be published on the main UI thread.
Just too much text. Please remove, there is a lot that is not needed.
Your design is complex, simplify it.
Why dont you just start 2 Async Tasks. Why have 2 jobs in 1 async task ? In one async task you can do your background thingy, and the other async task in the Pre and Post you can start your animation and stop your animation.
I have a settings tab in my program. The data you can set there, is not only changeable from this panel. That's why I want to reload this data like every 5 seconds. I think this has to be done with an extra Thread, but my knowledge about Threads is minimal. I already have a reload method for this purpose.
What should I use to do this (and how...)?
Use a ScheduledExecutorService:
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(yourRunnable, 5, 5, SECONDS);
Then reload your JPanel in yourRunnable (just follow the example from the JavaDocs).
but my knowledge about Threads is minimal...
You absolutely need to learn about threads in general, and the Java Concurrency Tutorial can help.
Then you should learn about concurrency in Swing in particular.
Draw your GUI's graphic representation of the data in your JPanel's paintComponent(...) method, or perhaps better, in a BufferedImage that is then displayed inside of paintComponent(...).
Reload the data in a background thread such as a SwingWorker. This Worker can have a java.util.Timer or a ScheduledExecutorService as per syb0rg's answer (1+ to syb0rg's answer) that requests and obtains new data every 5 seconds.
Then call repaint() from the Swing event thread after your data is changed. If using a SwingWorker, the process/publish method pair could help with this. You could publish your data to the Swing event thread with this.
You could use Timer to update periodically your data.
I need to execute an action after a specific amount of time (for example 30 minutes after the app started up, if the app is still up).
What are my options and will it necessary means there's going to be one thread "lost" waiting for the 30 minutes to pass by?
Ideally, at program startup, I'd like to do something like the following (simplified on purpose) and then don't have to think about it anymore:
doIfStillUp( 30, new Runnable() {
....
});
So how should I go about implementing doIfStillUp(...)?
Should I use a TimerTask? The Executor framework?
Most importantly (it's for understanding purpose): does this mean there's going to be one thread lost idling for basically nothing during 30 minutes?
If there's going to be one thread "doing nothing", is this an issue? What if there are 10 000 threads (I'm being facetious here) "doing nothing"?
Note that I'm trying to understand the "big picture", not to solve a particular problem.
The Executor framework is a reasonable choice.
There's a schedule method that just takes a runnable and a delay time.
schedule(Runnable command,
long delay,
TimeUnit unit)
That's pretty straightforward. There won't necessarily be a thread blocked waiting on your task. You could use a ScheduledThreadPoolExecutor, as linked above that keeps X threads ready to run scheduled tasks.
You can imagine a data structure that holds the time at which a task should be run. A single thread can watch or set up these delays and can potentially watch thousands of them in a single thread. When the first time expires it'll run the task. Potentially using its own thread, potentially using 1 of X in the thread pool. When a new task is added or an existing task is finished it'll wait for the earliest time to arrive and then start the whole process again.
You should use a Timer. Its javadoc answers all your questions.
One thread is used for every timer, but the timer executes several tasks, sequentially. The timer tasks should be very short. If they aren't, consider using several timers.
Of course, the timer thread will be idle if it doesn't have any task to execute. An idle thread doesn't consume anything (or nearly anything), so I wouldn't worry about it. Anyway, you don't have many choices. 10000 threads doing nothing would of course be an issue, but that would mean that you instantiated one timer per task, which is wrong.
You can schedule task on java.util.Timer. For all timer tasks single timer thread will be created by java.util.Timer.
The builtin java timer is the straight away solution: http://download.oracle.com/javase/1,5.0/docs/api/java/util/Timer.html#schedule(java.util.TimerTask, long)
I have a panel that displays text. I want the panel to change its text and then have the application pause before anything else happens. I'm using Thread.sleep(1000). For some reason, though, the application doesn't finish painting the panel before Thread.sleep gets called (the text doesn't get changed). I also tried this:
board.invalidate();
board.setLeftMessage("Not");
board.setRightMessage("Here");
board.revalidate();
Date current = new Date();
long timeNow = current.getTime();
Date newDate = new Date(timeNow + 1000);
while (current.before(newDate))
current = new Date();
but no luck there either. Anyone have a suggestion?
Thanks so much.
You are blocking the AWT Event Dispatch Thread (EDT). The EDT handle repainting and input events, so your code need not be multithreaded (which would be effectively impossible). Use javax.swing.Timer to send an event later on the EDT. (Do not confuse javax.swing.Timer with java.util.Timer!)
Take a look at javax.swing.Timer - I think this is what you'll need to accomplish this.
EDIT #1: The Sun\Oracle documentation actually suggests using Swing Timers from here.
In general, we recommend using Swing
timers rather than general-purpose
timers for GUI-related tasks because
Swing timers all share the same,
pre-existing timer thread and the
GUI-related task automatically
executes on the event-dispatch thread.
However, you might use a
general-purpose timer if you don't
plan on touching the GUI from the
timer, or need to perform lengthy
processing.
EDIT #2: It looks like there are some good basic tutorials here.
EDIT #3: Removed suggestion of using TimerTask - in this case it would be a bad idea.
Your "long running task" needs to run in a separate Thread. Then when you want to update the GUI you need the code to run on the Event Dispatch Thread (EDT). Then you can ask the separate Thread to sleep and it won't affect the painting of the GUI.
You should be able to use a SwingWorker for this. Read the section from the Swing tutorial on Concurrency for more information.
If you don't like using the SwingWorker, then you need to create your own Thread and wrap the code that updates the GUI in a SwingUtilities.invokeLater().
You shouldn't be updating GUI components from your main thread. Use SwingUtilities.invokeLater to schedule your update on the event thread:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
board.invalidate();
board.setLeftMessage("Not");
board.setRightMessage("Here");
board.revalidate();
}
};
Thread.sleep is a long running task. When you a running such a task in the EDT it blocks all repaint requests from being executed. All repaint requests which are pending and which were sent during the sleep phase are queued for future processing.
As a result when the EDT comes out of the sleep phase it coalesce all such repaint request (if coalescing is enabled which is the default property) into a single repaint which gets executed. If coalescing is not enabled then all queued request are executed serially without any time gap in between. As a result it seems that the UI did not update.
To correct the situation use a timer which triggers periodically after specific intervals of time.