I am trying to build more or less a watchdog class. If a value hasn't changed for a certain time, the class should do something.
Therefore I want to utilize a standard timer, the idea is to set a timer, and reset it as soon as the watched value changes.
public class Watchdog {
public final Timer TIMER = new Timer(true);
public final long DELAY;
public Watchdog(long delay){
DELAY=delay;
valueChanged();
}
public void valueChanged(){
TIMER.cancel();
TIMER.purge();
TIMER.schedule(new TimerTask() {
#Override
public void run() {
Watchdog.this.alarm();
}
}, DELAY);
}
public void alarm(){
System.out.println("Watchdog barked");
}
}
Unfortunately I get an IllegalStateException every time I call TIMER.cancel(), as there is no timer set at the first call. What am I doing wrong? How to reset the timer, even if there is no timer set?
EDIT
Stacktrace
Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled.
at java.util.Timer.sched(Timer.java:397)
at java.util.Timer.schedule(Timer.java:193)
at deleteme.Watchdog.valueChanged(Watchdog.java:28)
at deleteme.Watchdog.<init>(Watchdog.java:22)
at deleteme.Deleteme.main(Deleteme.java:19)
Java Result: 1
According to the Timer source code, the cancel method changes the value of an internal flag called newTasksMayBeScheduled to false.
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // In case queue was already empty.
}
}
The value of this flag is checked prior to scheduling a new task and it fires the exception you get as it is false.
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
I think that you should use the cancel method of the TimerTask instead of Timer.
For the sake of completeness of the answer, note that an alternative would be to use ScheduledThreadPoolExecutor.
Related
In essence, I have an onGuildMessageReceived method that listens for a command alarm with which a user can set a countdown to notify him with a message. They type a time amount and eventual alarm message that comes with the ping they will get after that time.
My question is, how can I make this feature work for each user separately, so other users don't overwrite previous alarm times that were already ticking?
This method is called in the main code to start the countdown.
public void StartTimer(int seconds, GuildMessageReceivedEvent event)
{
timer = new Timer();
timer.schedule(new TimerReminder(event), seconds * 1000);
}
public void StartTimer(int seconds, GuildMessageReceivedEvent event, String text)
{
timer = new Timer();
timer.schedule(new TimerReminder(event, text), seconds * 1000);
}
I have this subclass that does a block of code after the .schedule() time.
class TimerReminder extends TimerTask{
GuildMessageReceivedEvent eventtrigger;
String alarmText = "";
private TimerReminder(GuildMessageReceivedEvent event)
{
eventtrigger=event;
}
private TimerReminder(GuildMessageReceivedEvent event, String alarmArg)
{
eventtrigger=event;
alarmText=alarmArg;
}
public void run()
{
if(alarmText.isEmpty())
{
eventtrigger.getChannel().sendMessage("<#"+auth+">, ***your alarm is ringing!***🔔🔔🔔🔔").queue();
timer.cancel(); // Terminate the timer thread
}
else
{
eventtrigger.getChannel().sendMessage("<#"+auth+">:\n"+alarmText).queue();
timer.cancel(); // Terminate the timer thread
alarmText="";
}
}
}
At the end of each task, you're running cancel on the timer reference which cancels not the timer that is currently running (Unless no one else ran the command) but rather the timer object that is assigned at the moment to the timer reference which makes it seem like the timers are overwriting each other.
And even if you kept a reference to all of the timers so you could cancel them, there's no need for you to cancel the timer after the task completes, the timer terminates itself.
Furthermore, there's no need for you to keep a static reference to the timer that is currently running, or keep a reference to them at all. When you create a timer a background thread keeps a reference to the timer and the task so they won't get garbage collected. You'd be better off creating the timer as a local variable when the command is run, scheduling the timer and that's it.
If you want the ability to cancel the timers by a different command or whatever you have in mind, then you should keep a list or map with references to all the current timers so you can cancel them if needed.
I have scheduled a method to run at a certain date in the future; however, there are certain events that may or may not happen before that date that would mean I want to run the method earlier than the specified date; how can I do this? I currently have:
Timer timer = new Timer();
TimerTask task = new TaskToRunOnExpriation();
timer.schedule(task, myCalendarObject.getTime());
I will have many of these TimerTask's running in my application, stop specific instances of them if a certain even happens?
EDIT
I will only ever want to cancel a single Timer for a given event, is there a way of managing the identities for the Timers such that I can easily find and stop it?
If you have thousands of them you should use a ScheduledExecutorService which will pool threads rather than a Timer which will use one thread per timer.
The ScheduledFutures returned by the executor service when you schedule a task also have a cancel method to cancel the underlying tasks: future.cancel(true);.
As for cancelling the right task, you could store the futures in a Map<String, Future> so you can access them by name or id for example.
In C# I would say use delegates, but that is not an option in Java. I would work off this idea:
class Timers
{
Timer timer1;
Timer timer2;
ArrayList<Timer> timerList;
public Timers()
{
// schedule the timers
}
// cancel timers related to an event
public void eventA()
{
timer1.cancel();
timer2.cancel();
}
public void eventB()
{
for(Timer t : timerList)
t.cancel();
}
}
Use this schedule method.
public void schedule(TimerTask task,Date firstTime,long period)
task--This is the task to be scheduled.
firstTime--This is the first time at which task is to be executed.
period--This is the time in milliseconds between successive task executions
I use Timer in android to update a progress bar.Here is some of my code, hoping it can help you:
Timer timer ;
#Override
protected void onCreate(Bundle savedInstanceState) {
//....
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
updateLogoBarHandler.sendEmptyMessage(0);
Log.e("SplashActivity","updating the logo progress bar...");
}}, 0, 50);
//.....
}
//here do the timer.cancel();
private Handler updateLogoBarHandler = new Handler() {
public void handleMessage(Message msg) {
if(logobarClipe.getLevel() < 10000){
logobarClipe.setLevel(logobarClipe.getLevel() + 50);
}else{
timer.cancel();
}
super.handleMessage(msg);
}
};
I am trying to schedule a repeating timer in GWT, it will run every one milli second, poll for a certain event, if it is found satisfactory, do something and cancel the timer.
I tried doing this:
final Timer t = new Timer() {
public void run() {
if (..condition is true, exit) {
t.cancel();
doSomething();
}
}
}
t.scheduleRepeating(1);
However, I get an error message like the local variable t may not have been initialized. I am putting tis piece of code in the onSuccess clause of a RequestBuilder callback.. How do I achieve this?
You cannot access it while intializing itself.
change your code to
final Timer fgf = new Timer() {
#Override
public void run() {
cancel();
System.out.println();
}
};
My CheckStatus (TimerTask) is not stopping when I call cancel() inside the run method. this.cancel() also does not work. What am I doing wrong?
import java.util.TimerTask;
class CheckStatus extends TimerTask {
int s = 3;
public void run() {
if (s > 0)
{
System.out.println("checking status");
s--;
}
else
{
System.out.println("cancel");
cancel();
}
}
}
Heres my driver
import java.util.*;
class Game {
static Timer timer;
static CheckStatus status;
public static void main(String[] args) throws InterruptedException {
CheckStatus status = new CheckStatus();
timer = new Timer();
timer.scheduleAtFixedRate(status, 0, 3000);
}
}
It works for me - using exactly the code you posted, I saw:
checking status
checking status
checking status
cancel
... and nothing else, showing that the TimerTask has been cancelled - it's never executing again.
Were you expecting the Timer itself to shut down when it didn't have any more work to do? It doesn't do that automatically. It's not clear what your bigger goal is, but one option to avoid this is to make the TimerTask also cancel the Timer - that will work, but it's not terribly nice in terms of design, and means that the TimerTask can't run nicely within a timer which contains other tasks.
you are calling the cancel method of the timer task but you should call the cancel method of the timer itself to get your expected behaviour.
I develop a simple application and I use timer, but if I run the timer several times the timer drops this exception: Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Timer already cancelled.
Here is my code:
public class Main {
...
private static void createAndShowUI() {
...
//a listener of a radio button
ActionListener on_action = new ActionListener() {
public void actionPerformed(ActionEvent e) {
Timer.timer.schedule(Timer.task,0,2000); //I call the timer here
}
};
...
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
//and the class of timer:
public class Timer {
public static java.util.Timer timer = new java.util.Timer();
public static java.util.TimerTask task = new java.util.TimerTask() {
public void run() {
//some tasks
}
};
}
My question is that where I use the thread? Thanks!
The problem is not using the Event-Queue thread, it is that you are re-using a cancelled Timer.
I'm guessing you are using the Timer to do some animation or something in response to a button push (as you schedule things at a fixed rate). I'm guessing also that in code you don't show us, the timer gets cancelled by a separate event. If you ever call Timer.cancel() can you show us that code?
From the exception what is happening is that you are trying to use the same Timer that you have already cancelled. Once a Timer has been cancelled, it can't be used again.
Two suggestions - use a different Timer every time. Also, if you are doing things for UI purposes, you might want to consider using a Swing timer instead.
As far as the Thread goes, all GUI events happen on the AWT Thread, but I repeat, this is almost certainly not the problem. Read this for more details.
A timer goes into cancelled state if either the cancel() method has been invoked or the if the timer task has terminated unexpectedly:
If the timer's task execution thread terminates unexpectedly, for example, because its stop method is invoked, any further attempt to schedule a task on the timer will result in an IllegalStateException, as if the timer's cancel method had been invoked.
So in your case, it may not be a problem of where you put/call/use your time, but more a problem of what you're actually doing with your timer.
Here you have your thread:
Corresponding to each Timer object is a single background thread that is used to execute all of the timer's tasks, sequentially
so if you try to access your GUI from the Timer task you should put it into the EventQueue thread.
And look here
If the timer's task execution thread terminates unexpectedly, for example, because its stop method is invoked, any further attempt to schedule a task on the timer will result in an IllegalStateException, as if the timer's cancel method had been invoked.
Do you let the Timer schedule any more tasks after it has been cancelled?