if(e.getSource()==continuous)
{
TimerTask task = new TimerTask()
{
public void run()
{
rollthedice();
}
};
timer.schedule(task, java.util.Calendar.getInstance().getTime(), 500);
}
if(e.getSource()==stop)
{
timer.cancel();
}
i hit the continuous button, rollthedice() executes looping twice a second, i hit the stop button rollthedice() stops, what i been looking for is a way to hit the continuous button again after i hit stop to start looping the rollthedice() method again, the stop is to stop the continuous cycle of rollthedice() but i want to be able to hit the continuous button again, idk how to do it, i been looking and looking
Updated thoughts:
Runnable runner = new Runnable(){
public void run()
{
rollthedice();
}
}
if(e.getSource()==continuous)
{
future = scheduler.scheduleAtFixedRate(runner, 0, 500, TimeUnit.MILLISECONDS);
}
if(e.getSource()==stop)
{
future .cancel();
}
From the javadoc for Timer.cancel():
Terminates this timer, discarding any currently scheduled tasks. Does not interfere with a currently executing task (if it exists). Once a timer has been terminated, its execution thread terminates gracefully, and no more tasks may be scheduled on it.
This means a new instance of Timer will be required to execute rollthedice() method again.
You could use ScheduledThreadPoolExecutor.scheduleAtFixedRate instead. This will allow you to submit a task, cancel it and then submit again.
Related
I am working on a project that requires me to filter through a long list of contacts by name based on a query entered by the user. The user can enter and delete characters while I am still filtering the list. For example, I might have a list containing 5000 contacts:
FirstName1 LastName1
FirstName2 LastName2
...
FirstName5000 LastName5000
The user has a form where he/she can enter search criteria and the list should shrink to show only those contacts that meet the search criteria. Here is the problem I have, if the user enters say
J
I should filter the list and only show the contacts whose first name OR last name start with 'J'. However, the user might then enter another character or delete characters, in which case I need to restart the filtering of the list. My issue of course is that I want to do this in an efficient way and not wait until the filtering is done with the letter 'J' before I start filtering with a new criteria. Any ideas/recommendations?
To avoid launching too many queries which should help to be more scalable, I would suggest to implement a mechanism that waits a given amount of time before launching the query. Anytime the user modifies the content of the field during this time frame, it will abort the previous query and will schedule a new query.
Something like that:
Code that creates the Timer and schedule the task:
Timer timer = new Timer();
// Schedule my task to be executed in 200 milliseconds
timer.schedule(new TimerTask() {
#Override
public void run() {
// Launch my query here
}
}, 200L);
Code to cancel the previous scheduled task: (to be launched any time the user modifies something)
// Cancel the previous timer which will also abort the scheduled task
timer.cancel();
// Create a new timer
timer = new Timer();
// Re-schedule the task
timer.schedule(new TimerTask() {
#Override
public void run() {
// Launch my query here
}
}, 200L);
It can also be done with a ScheduledExecutorService as next:
Code that creates the ScheduledExecutorService and schedule the task:
// Create the ScheduledExecutorService
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// Submit the task to be executed in 200 milliseconds
ScheduledFuture<?> future = executor.schedule(new Runnable() {
#Override
public void run() {
// Launch my query here
}
}, 200, TimeUnit.MILLISECONDS);
Code to cancel the previous scheduled task: (to be launched any time the user modifies something)
// Cancel the task which will interrupt the thread that was executing the
// task if any
future.cancel(true);
// Re-submit the task
future = executor.schedule(new Callable<Void>() {
#Override
public Void call() throws InterruptedException {
...
// Check regularly in your code if the thread has been
// interrupted and if so throws an exception to stop
// the task immediately
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException("Thread interrupted");
}
...
}
}, 200, TimeUnit.MILLISECONDS);
NB: Those code snippets are only meant to show the idea, they are not meant to be perfect
ok, so basically you need to run your query on a background thread and cancel a currently running query if the user changes the input and start a new one.
first we need a task class, that will wrap your query:
class CancelableTask implements Callable<Void> {
//need this to know, if the task was canceled
private Future<Void> myFuture;
public void setMyFuture(Future<Void> myFuture) {
this.myFuture = myFuture;
}
#Override
public Void call() throws Exception {
//we run a loop until the query is finished or task was canceled
while (!this.myFuture.isCancelled() && !myQuery.isFinished()) {
//the step should be small enough to fast detect task cancellation but big enough to avoid too much overhead
myQuery.performQueryStep();
}
if(!this.myFuture.isCancelled()){
//query is finished and task wasn't canceled, so we should update UI now
updateUIOnUIThread(myQuery.result());
}
return null;
}
}
now you need to create ExecutorService somewhere in your activity:
//1 Thread should be enough, you could use 2 Threads if your query-step is quite long and you want to start the following query faster
private ExecutorService executor = Executors.newSingleThreadExecutor();
now we can use executor to run the tasks. This code should be called as soon as user changed the input. It should be called on the UI-Thread to avoid problems with setting currentTaskFuture:
//check if need to cancel the currentTask
if(currentTaskFuture != null && !currentTaskFuture.isDone()){
currentTaskFuture.cancel(false);
}
CancelableTask task = new CancelableTask();
//submit the task
Future<Void> future = executor.submit(task);
task.setMyFuture(future);
//set current task's future so we can cancel it if needed
currentTaskFuture = future;
I'm using Timer() due to its accuracy but works in the same was as PostDelayed Handler. It's called only once. Here is the Timer code:
public void setWFT() {
WFT = new Timer();
WFT.schedule(new TimerTask() {
#Override
public void run() {
WFTTimerMethod();
}
}, 60000); // 60 seconds delay
}
private void WFTTimerMethod() {
this.runOnUiThread(Timer_Tick);
}
private Runnable Timer_Tick = new Runnable() {
public void run() {
// My commands here
}
};
This only calls run() once after 60 seconds once the Timer is started. Sometimes, I have to cancel the Timer to Update the delay (replace the "60000" value). To start the Timer again, I simply recreate the Timer by calling WFT() again with the new delay value.
Problem is, when I cancel the timer using:
WFT.cancel();
WFT.purge();
The Timer does not start. the run() doesn't execute when it's supposed to. So my question is do I use cancel() and purge() or just cancel()?
Thanks
From the Java API on purge():
Most programs will have no need to call this method. It is designed for use by the rare application that cancels a large number of tasks. Calling this method trades time for space: the runtime of the method may be proportional to n + c log n, where n is the number of tasks in the queue and c is the number of cancelled tasks.
So you only need to call cancel()
from cancel() documentation :
No more tasks may be scheduled on this Timer.
I just solved the problem myself. I had multiple calls for syncCustomers() due to a dialog closing event problem. I solved it by providing the parent JFrame in the JDialog constructor. Pretty stupid error on my side.
My application contains a task that synchronizes with a webservice and a local database. This task may take up to several minutes. Thus I want to notify the user about this time consuming process with a simple dialog (Swing). The user is not supposed to continue working while the sync process is running.
So I thought of:
open modal dialog with the notification for the user
start the sync process in a separate thread
close modal dialog after sync process is done
User clicked on the button to start sync process:
private void syncCustomers() {
if (checkWebserviceAuth()) {
SyncDialog dialog = new SyncDialog();
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
SyncCustomersTask task = new SyncCustomersTask(dialog, getCoach());
task.run(); // task.start() will result in the same problem
} else {
openAuthorizeDialog(true);
}
}
public class SyncDialog extends javax.swing.JDialog {
public SyncDialog() {
initComponents();
// I already noticed that the modal dialog won't work for me since it interrupts within syncCustomers()
//this.setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
this.setTitle(Application.getApplicationTitle());
}
...
}
public class SyncCustomersTask extends Thread {
private void doWork() {
System.out.println("Start doWork() and sleep for 10 seconds...");
try {
// for testing purpose
Thread.sleep(10000);
} catch (InterruptedException ex) {
}
System.out.println("Done with doWork().");
}
#Override
public void run() {
doWork();
if (getCallback() != null) {
System.out.println("Invoke callback...");
getCallback().dispose();
System.out.println("Callback invoked.");
}
}
...
}
This will result in an infinite loop of:
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
If I comment out
getCallback().dispose();
, the loop will stop after the second execution:
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
Start with doWork()...
Start doWork() and sleep for 10 seconds...
Done with doWork().
Invoke callback...
Callback invoked.
I don't get it. What fires the thread to execute over and over again?
I guess this whole thing isn't a good idea to start with, but I wasn't able to get things like ProgressMonitor working either. :(
Call start(), not run(). The latter will simply execute the thread, but not in a separate thread! The start() method will instantiate a new thread, and only then invoke your run() method in that new thread.
This is a surprising common problem, btw.
invoking run() does not execute code in a new thread.
My situation is, I have two concurrent threads, one that cant start a timer and the other can stop the timer. The timer works in a way such that, once it has started it will count to 5 seconds and execute a function after, it will keep doing this until the timer is stopped by the other thread. How can this be implemented in Java. This is what I have, I feel it is the wrong way of doing it:
Note that sleep is a global volatile variable that the other two threads turn on and off.
void creatTime(final EventHandler handler)
{
Thread timer = new Thread()
{
public void run()
{
try
{
while(true)
{
while(sleep) Thread.sleep(1000);
//invoke function
}
}
catch(IOException e)
{
System.out.println(e);
}
}
};
timer.start();
}
}
You can create a TimerTask and schedule it to run every 5 seconds
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
// TODO Auto-generated method stub
//Invoke your function here
}
};
//Create a Timer and schedule it
Timer timer = new Timer();
timer.scheduleAtFixedRate(timerTask, 0, 5*1000);
//To terminate the Timer you can call its cancel method.
I agree with the TimerTask recommendation. In general, the more of your thread-related work you can pass on to the higher level features of java.util.concurrent etc. the better. For example, the original code does not deal with early wake-ups from sleep.
In addition, if the sleep variable remains after redesign, it needs to be marked volatile or accessed through synchronized get and set methods. There is a limited set of activities that ensure that writes done in one thread must become visible to reads done in another thread.
I have a scenario where I need to run a certain task at specific interval, however, I want to be able to reset/restart the timer without reinstantiating. Here's my code for better understanding.
private TimerTask beatTask = new TimerTask() {
#Override
public void run() {
beatDetected();
}
};
public void beatDetected() {
timeoutTimer.cancel();
// handle my stuff and restart the timer.
timeoutTimer.schedule(beatTask, 2000);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
(timeoutTimer = new Timer()).schedule(beatTask, 2000);
return Service.START_STICKY;
}
The idea behind this implementation is that beatDetected() can be called from outside event, in which case the next timer tick should happen from that moment on i.e. the elapsed time for the next task should be reset. However, I only get the first tick, and from that point on, the timer just doesn't work.
I'm not limited to using Timer class, anything that will solve the above scenario will work. I was thinging about using postDelayed, but this code is located in a Service, and I don't really need UI thread aware updates.
Using separate thread for timer is considered a somewhat bad practice on Android devices. That's because you are going to waste resources in most scenarios.
If you don't need super precise timing events, you should go with Handler based timers. An example of such timer can be seen here: Repeat a task with a time delay?. This approach works both for Activities and Services.
Also keep in mind that both Handler and Timer based timers will be paused if device goes to sleep mode. If this is not what you need, then use AlarmManager (but keep in mind that using AlarmManager incorrectly may lead to very bad battery performance).
Reseting the Handler based timer:
void resetRepeatingTask() {
mHandler.removeCallbacks(mBeatDetector);
mHandler.postDelayed(mBeatDetector, mInterval);
}
The accepted answer does not work (at least not for me).
A simple solution uses a Timer to trigger a TimerTask, which can easily be reset.
Log.d(TAG, "Restarting timer");
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Log.d(TAG, "Run the task");
}
}, 1000);
Note that a new TimerTask must be created every time you reset the timer - you can't reuse the old one. If you want to preserve state you will need to get the timer task to run a separate task.