I have this java code using Threads to calculate the time elapsed once the start button is hit till the stop button is not hit.
I want to do this using Threads only
import javax.swing.*;
import java.awt.event.*;
// This will count the elapsed time between running time of two threads.
class ThreadGame {
JButton button;
MyAction my_action;
public static void main(String[] args) {
ThreadGame tg = new ThreadGame();
}
public ThreadGame() {
JFrame frame = new JFrame("Calculate time - Game");
button = new JButton("Start");
button.addActionListener(new MyAction());
frame.add(button);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class MyAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String text = (String) e.getActionCommand();
final Timer timer = new Timer();
if (text.equals("Start")) {
button.setText("Stop");
Thread start_time = new Thread() {
public void run() {
timer.startTime();
}
};
start_time.start();
try {
start_time.join();
} catch (Exception e1) {
}
} else {
Thread stop_time = new Thread() {
public void run() {
timer.stopTime();
}
};
Thread get_time = new Thread() {
public void run() {
timer.getElapsedTime();
System.out.println(timer.elapsed);
}
};
stop_time.start();
try {
stop_time.join();
} catch (Exception e2) {
}
get_time.start();
button.setText("Start");
}
}
}
class Timer {
public double startTime = 0.0;
public double stopTime = 0.0;
public boolean running = false;
public double elapsed = 0.0;
public void startTime() {
this.startTime = System.nanoTime();
this.running = true;
}
public void stopTime() {
this.stopTime = System.nanoTime();
this.running = false;
}
// Elasped time in seconds
public double getElapsedTime() {
// double elapsed;
if (running) {
elapsed = ((System.nanoTime() - startTime) / 1000);
} else {
elapsed = ((stopTime - startTime) / 1000);
}
return elapsed;
}
}
}
EDIT: I have understand the problem: timer scope was the problem.
EDIT 2: Ok, it looks like I have to use suspend and resume in one thread only.
The problem is that the start button press is starting a different Timer object than the stop-button button press is stopping because the Timer object is created every time when the actionPerformed(...) method is called.
The Timer needs to be a field in your MyAction class. You also don't need all of the thread start/joins because the Timer object is very simple and fast.
Really, you can just use a startTimeMillis long field instead of a timer. Something like:
class MyAction implements ActionListener {
private long startTimeMillis;
public void actionPerformed(ActionEvent e) {
String text = (String) e.getActionCommand();
if (text.equals("Start")) {
startTimeMillis = System.currentTimeMillis();
...
} else {
System.out.println(System.currentTimeMillis() - startTimeMillis);
}
}
}
Your problem is caused by the scope of timer. This should be a private instance variable, not a local method variable. Further, wrapping calls to startTime and endTime in a thread's run method isn't gaining you anything, because these are incredibly short-lived calls. But that's not the real problem here.
There's no reason to be running Timer in its own thread. That is, without using a specialized real-time operating system, using threads to solve the problem of measuring the duration between two events is just plain wrong.
You might think that you could create a thread with a loop that increments a msec variable after a Thread.sleep(1). Don't do this! This kind of timing is also just plain wrong. Your computer uses a preemptive multitasking model which means there's no guarantee that your thread will execute on a regular interval. That is, there is nothing requiring that Thread.sleep(1) will sleep for some maximum duration. The guarantee is that your thread will sleep for a minimum of 1ms. This means there's no way to determine clock error if you're managing a clock yourself in software, and this error is not insignificant. Just don't do it!! Time should always be measured by your operating system, preferably using an interrupt-based timer (which is how System.nanoTime works on most, if not all platforms).
Instead of using a thread, just call your startTime and stopTime methods directly from your original thread.
Try this:
class ThreadGame {
JButton button;
MyAction my_action;
private final Timer timer = new Timer();
public static void main(String[] args) {
ThreadGame tg = new ThreadGame();
}
public ThreadGame() {
JFrame frame = new JFrame("Calculate time - Game");
button = new JButton("Start");
button.addActionListener(new MyAction());
frame.add(button);
frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class MyAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
String text = (String) e.getActionCommand();
if (text.equals("Start")) {
timer.startTime();
button.setText("Stop");
} else {
timer.stopTime();
button.setText("Start");
}
}
}
class Timer {
public double startTime = 0.0;
public double stopTime = 0.0;
public boolean running = false;
public double elapsed = 0.0;
public void startTime() {
this.startTime = System.nanoTime();
this.running = true;
}
public void stopTime() {
this.stopTime = System.nanoTime();
this.running = false;
}
// Elasped time in seconds
public double getElapsedTime() {
return (this.startTime-this.stopTime)*1000000000.0;
}
}
}
If you want to learn how to use threads, try writing an application that solves a problem for which threads are a good fit. For example, write a small Swing application that lets you download a file from the web. As the file is downloading, update a progress bar in your UI. The download should happen in a worker thread separately from the UI thread, otherwise the UI will block during the download's progress.
Another example problem is to write a threaded ray tracer (here's an example tutorial written in C++).
If you want something simpler, write a Swing clock. Use a loop within a separate thread to update the UI at a periodic interval, but do not use the loop to manage what time it is. Again, don't try to keep time in the loop, just let the OS keep the time, and use the loop to schedule when the UI gets updated with the current OS time.
You're never calling getElapsedTime() that updates elapsed field.
You are creating a new Timer when ever you click the button. Make timer a class variable of your MyAction
The code below should be sufficient to get elapsed time.
class MyAction implements ActionListener {
final Timer timer = new Timer();
public void actionPerformed(ActionEvent e) {
String text = (String) e.getActionCommand();
if (text.equals("Start")) {
button.setText("Stop");
timer.startTime();
} else {
timer.stopTime();
System.out.println(timer.elapsed);
button.setText("Start");
}
}
}
Simply do this...
- Call System.currentTimeMillis() at the Starting of threads.
- Then again call System.currentTimeMillis() at the end of threads.
- Subtract the end with starting value to get the Elapsed time...
/////////////////EDITED///////////////////////
Make sure that the method trying to manipulate(ie. read and write) the variable holding the System.currentTimeMillis() must be synchronized, with synchronized keyword, so that Race Condition doesn't occur according to Brian's Law...
If you are writing a variable that might next be read by another thread, or reading a variable that might have last been written by another thread, you must use synchronization, and further, both the reader and the writer must synchronize using the same monitor lock.
Related
I am trying to develop a means of scheduling a Runnable after a least amount of time has elapsed.
The code should start with a request being made and count down until an amount of time has elapsed, and then execute the Runnable.
But I also need that there can be more than one requests made, and for each new request the delay will be renewed before the Runnable is executed.
The goal is to achieve the following behaviour:
When the user scrolls a JList, an adjustment listener in the vertical scroll-bar of the JList's JScrollPane will request the delay before the Runnable is executed.
Each time the user scrolls a new request is made, so the delay is renewed.
The request returns immediately so that the EDT is blocked for the least amount of time.
So the waiting and executing of the Runnable should occur in a different Thread (than the EDT).
After a least amount of time has elapsed, from the last made request, the Runnable is executed.
I need this behaviour because the JList will contain many thousands of thumbnails of images.
I don't want to pre-load all the thumbnails in the JList because they might not fit into memory.
I don't want to load thumbnails as the user scrolls either, because he can make arbitrary fast scrolls let me put it.
So I only want to start loading thumbnails after the user waits/settles in a single location in the JList for an amount of time (say for example 500 ms, 1 second, or something between).
What I have tried is to create an entirely handmade scheduler with worker Threads.
Follows my effort, with relative explanations in the comments:
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
public class SleepThenActScheduler {
public class WorkerThread extends Thread {
//How long will we be waiting:
private final TimeUnit sleepUnit;
private final long sleepAmount;
public WorkerThread(final TimeUnit sleepUnit,
final long sleepAmount) {
this.sleepUnit = sleepUnit;
this.sleepAmount = sleepAmount;
}
public TimeUnit getSleepUnit() {
return sleepUnit;
}
public long getSleepAmount() {
return sleepAmount;
}
#Override
public void run() {
try {
if (sleepUnit != null)
sleepUnit.sleep(sleepAmount); //Wait for the specified time.
synchronized (SleepThenActScheduler.this) {
if (t == this && whenDone != null) { //If we are the last request:
//Execute the "Runnable" in this worker thread:
whenDone.accept(System.currentTimeMillis() - start);
//Mark the operation as completed:
whenDone = null;
t = null;
}
}
}
catch (final InterruptedException ix) {
//If interrupted while sleeping, simply do nothing and terminate.
}
}
}
private LongConsumer whenDone; //This is the "Runnable" to execute after the time has elapsed.
private WorkerThread t; //This is the last active thread.
private long start; //This is the start time of the first request made.
public SleepThenActScheduler() {
whenDone = null;
t = null;
start = 0; //This value does not matter.
}
public synchronized void request(final TimeUnit sleepUnit,
final long sleepAmount,
final LongConsumer whenDone) {
this.whenDone = Objects.requireNonNull(whenDone); //First perform the validity checks and then continue...
if (t == null) //If this is a first request after the runnable executed, then:
start = System.currentTimeMillis(); //Log the starting time.
else //Otherwise we know a worker thread is already running, so:
t.interrupt(); //stop it.
t = new WorkerThread(sleepUnit, sleepAmount);
t.start(); //Start the new worker thread.
}
}
And the usage of it will seem like the following code (which I would like to remain relevant in your possible answers if possible):
SleepThenActScheduler sta = new SleepThenActScheduler();
final JScrollPane listScroll = new JScrollPane(jlist);
listScroll.getVerticalScrollBar().addAdjustmentListener(adjustmentEvent -> {
sta.request(TimeUnit.SECONDS, 1, actualElapsedTime -> {
//Code for loading some thumbnails...
});
});
But this code creates a new Thread for each request (and interrupts the last one).
I don't know if this is a good practice, so I have also tried using a single Thread which loops sleeping until the requested time has elapsed from the last made request:
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.LongConsumer;
public class SleepThenActThread extends Thread {
public static class TimeAmount implements Comparable<TimeAmount> {
private final TimeUnit unit;
private final long amount;
public TimeAmount(final TimeUnit unit,
final long amount) {
this.unit = unit;
this.amount = amount;
}
public void sleep() throws InterruptedException {
/*Warning: does not take into account overflows...
For example what if we want to sleep for Long.MAX_VALUE days?...
Look at the implementation of TimeUnit.sleep(...) to see why I am saying this.*/
if (unit != null)
unit.sleep(amount);
}
public TimeAmount add(final TimeAmount tammt) {
/*Warning: does not take into account overflows...
For example what if we want to add Long.MAX_VALUE-1 days with something else?...*/
return new TimeAmount(TimeUnit.NANOSECONDS, unit.toNanos(amount) + tammt.unit.toNanos(tammt.amount));
}
#Override
public int compareTo(final TimeAmount tammt) {
/*Warning: does not take into account overflows...
For example what if we want to compare Long.MAX_VALUE days with something else?...*/
return Long.compare(unit.toNanos(amount), tammt.unit.toNanos(tammt.amount));
}
}
private static TimeAmount requirePositive(final TimeAmount t) {
if (t.amount <= 0) //+NullPointerException.
throw new IllegalArgumentException("Insufficient time amount.");
return t;
}
private LongConsumer runnable;
private TimeAmount resolution, total;
public SleepThenActThread(final TimeAmount total,
final TimeAmount resolution) {
this.resolution = requirePositive(resolution);
this.total = requirePositive(total);
}
public synchronized void setResolution(final TimeAmount resolution) {
this.resolution = requirePositive(resolution);
}
public synchronized void setTotal(final TimeAmount total) {
this.total = requirePositive(total);
}
public synchronized void setRunnable(final LongConsumer runnable) {
this.runnable = Objects.requireNonNull(runnable);
}
public synchronized TimeAmount getResolution() {
return resolution;
}
public synchronized TimeAmount getTotal() {
return total;
}
public synchronized LongConsumer getRunnable() {
return runnable;
}
public synchronized void request(final TimeAmount requestedMin,
final LongConsumer runnable) {
/*In order to achieve requestedMin time to elapse from this last made
request, we can simply add the requestedMin time to the total time:*/
setTotal(getTotal().add(requestedMin));
setRunnable(runnable);
if (getState().equals(Thread.State.NEW))
start();
}
#Override
public void run() {
try {
final long startMillis = System.currentTimeMillis();
TimeAmount current = new TimeAmount(TimeUnit.NANOSECONDS, 0);
while (current.compareTo(getTotal()) < 0) {
final TimeAmount res = getResolution();
res.sleep();
current = current.add(res);
}
getRunnable().accept(System.currentTimeMillis() - startMillis);
}
catch (final InterruptedException ix) {
}
}
}
(Note: the second approach is not fully debugged, but I think you get the idea.)
And the usage of it will seem like the following code:
SleepThenActThread sta = new SleepThenActThread(new TimeAmount(TimeUnit.SECONDS, 1), new TimeAmount(TimeUnit.MILLISECONDS, 10));
final JScrollPane listScroll = new JScrollPane(jlist);
listScroll.getVerticalScrollBar().addAdjustmentListener(adjustmentEvent -> {
sta.request(new TimeAmount(TimeUnit.SECONDS, 1), actualElapsedTime -> {
//Code for loading some thumbnails...
});
});
But I don't know if this is a good practice either, and this is also consuming more CPU time I guess.
My question though is not for the most ecological solution, but is if there exists a better/more-formal way of achieving this with less commotion/code.
For example should I use a java.util.Timer, a javax.swing.Timer, or a ScheduledExecutorService? But how?
I'm guessing something in the java.util.concurrent package should be an answer.
I don't really care about super accuracy in the delay as you can imagine.
Any recommendations in the comments about other approaches to achieve the same goal would also be good.
I am not really asking for debugging, but I also don't think this question should be moved to Code Review because I'm asking for an alternative/better solution.
I would prefer this to be in Java 8 (and above, if not possible with 8).
Thank you.
Here's an example of using a Swing timer. Pressing the button will restart the 2-second delay.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Delay extends JPanel {
Timer timer;
int presses = 0;
public Delay() {
setLayout(new BorderLayout());
JButton b = new JButton("Sleep 2 seconds");
JLabel label = new JLabel("The app is currently asleep.");
add(b, BorderLayout.CENTER);
add(label, BorderLayout.SOUTH);
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
timer.restart();
presses++;
}
});
timer = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
label.setText("Time expired after " + presses + " presses");
}
});
timer.start();
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JFrame jf = new JFrame();
JPanel panel = new Delay();
jf.add(panel);
jf.pack();
jf.setVisible(true);
jf.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(final WindowEvent arg0) {
System.exit(0);
}
});
}
});
}
}
So I have a game that I'm trying to make and in the game loop, I call Thread.sleep(). Else where, I have code that maintains the aspect ratio of the window when resizing. This works great, except that I get weird flickering when I'm resizing. I've narrowed the problem down to Thread.sleep(), when I take this line out, my program works just as expected, but this causes the CPU to spike so high that on my Macbook, the Activity Monitor app says my game is using 170+%! Now this is problematic and exactly why I put the sleep line in there anyway. I've heard that sleeping on the event dispatch thread will cause this effect, but I am running this loop in a new thread, so I thought I was good. Do you guys know what could be going on? Here's part of the source code (you really need to look at the run() method):
package jeffrey_ryan.game2d;
public class GameLoop implements Runnable {
private boolean running = false;
private boolean paused = false;
private float gameHertz = 30.0f;
private long timeBetweenUpdates = (long) (1_000_000_000 / gameHertz);
private int maxUpdates = 5;
private LoopListener loopListener;
public void run() {
long lastUpdateTime = System.nanoTime();
running = true;
while (running) {
long now = System.nanoTime();
if (!paused) {
int updates = 0;
while (now - lastUpdateTime >= timeBetweenUpdates && updates < maxUpdates) {
if (loopListener != null) {
loopListener.update((double) timeBetweenUpdates / 1_000_000_000);
}
lastUpdateTime += timeBetweenUpdates;
updates++;
}
if (loopListener != null) {
float interpolation = Math.min(1.0f, (float) (now - lastUpdateTime) / timeBetweenUpdates);
loopListener.render(interpolation);
}
long timeRemaining = (timeBetweenUpdates - (now - lastUpdateTime)) / 1_000_000;
try {
Thread.sleep(Math.max(timeRemaining - 5, 0)); // HERE'S THE OFFENDING LINE ******************
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
}
else {
try {
Thread.sleep(25);
}
catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
public void start() {
running = true;
}
public void stop() {
running = false;
}
public void pause() {
paused = true;
}
public void play() {
paused = false;
}
public float getSpeed() {
return gameHertz;
}
public void setSpeed(float hertz) {
gameHertz = hertz;
timeBetweenUpdates = (long) (1_000_000_000 / gameHertz);
}
public int getMaxUpdates() {
return maxUpdates;
}
public void setMaxUpdates(int updates) {
maxUpdates = updates;
}
public void setLoopListener(LoopListener listener) {
loopListener = listener;
}
}
In my subclass of JPanel, here's the code that runs this loop (Where the loop variable is an instance of the above class):
#Override
public void addNotify() {
super.addNotify();
addKeyListener(this);
addMouseListener(this);
Thread thread = new Thread(loop, "GameLoop");
thread.start();
}
If you guys could help me I would love it, I'm really stumped. Thanks!
You should use SwingWorker instead of a Thread to manipulate Swing components asynchronously. When I discovered this guy my life changed =). The SwingWorker gives you a method "process" which you can use to make actions gradually, and a "done" method to finish your processing, both of these methods are safe to handle the event dispatch thread. The background process you should make on "doInBackground".
Calling 'Thread.sleep(n)' causes the whole thread to become unresponsive, if this thread is tied to your JFrame thread then that thread will also become unresponsive and cause the whole frame and component to freeze and stop responding -- probably the reason for the flickering. So make sure the sleep is in game loop and not on the frame, one way to do this is create two threads at initialization, one for the frame and the other for the logic, then just let the game loop handle input and output while the display thread simply displays (i believe this how most game engines work). Also make sure neither thread is linked in any or the sleeping thread will affect the display thread.
I found the answer to my problem. The class that was calling the loop, which was a JPanel, didn't repaint when resized, only when the loop told it to, which caused some periods where the JPanel wasn't painted too. I fixed this by overriding paintComponent.
I need to make a GUI where a worker enters a station (a spot on the panel) and stays there for a set amount of seconds, shown in a countdown about the workers head (so, once the workers moves to the spot, the station's label shows 3s -> 2s -> 1s and then the worker leaves, and the label reverts back to "OPEN"). I'm having trouble with making this happen, as I'm not too good with the Timer(s?) that Java has. I tried with something like this:
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
//change label text/color, decrement countdown
panel.repaint();
Thread.sleep(1000);
}
});
But I can't reach the number of seconds to count down from from inside the timer, and I'm not sure how to pass that value to the timer. If someone can help me out, I'd really appreciate it.
Get rid of the Thread.sleep(). That's what the 1000 in Timer(1000, new ActionListener() does. It sets an interval for each timer event. Every time a timer event is fired, the actionPerformed is called. So you need to determine what needs to happen every "tick", and put that code in the actionPerformed. Maybe something like
Timer timer = new Timer(1000, new ActionListener() {
private int count = 5;
#Override
public void actionPerformed(ActionEvent e) {
if (count <= 0) {
label.setText("OPEN");
((Timer)e.getSource()).stop();
count = 5;
} else {
label.setText(Integer.toString(count);
count--;
}
}
});
You need to decide when to call timer.start().
For general information, see How to Use Swing Timers
Problem #1: You are calling Thread.sleep() from within the Swing GUI thread. That causes the thread to stop taking input and freeze. Delete that line. It does you no good! While you are at it, delete the repaint call as well.
Now that that's said and done, instead of creating an anonymous instance of ActionListener, you can create an actual class that implements ActionListener and provides a constructor. That constructor can have as an argument the number of seconds you want to start counting down. You can declare that class inside the method you are using, or you can declare it inside the class.
Here's a skeletal example:
public class OuterClass {
JLabel secondsLabel = ...;
Timer myTimer;
private void setupTimer(int numSecondsToCountDown) {
secondsLabel.setText(Integer.toString(numSecondsToCountDown));
myTimer = new Timer(1000, new CountdownListener(numSecondsToCountDown));
myTimer.start();
}
// ...
class CountdownListener implements ActionListener {
private int secondsCount;
public CountdownListener(int startingSeconds) { secondsCount = startingSeconds; }
public void actionPerformed(ActionEvent evt) {
secondsLabel.setText(Integer.toString(secondsCount);
secondsCount--;
if (secondsCount <= 0) { // stop the countdown
myTimer.stop();
}
}
}
}
private class MultipleGensListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
for(int i = 0; i < 25; i++)
{
game.runSimulationOneGen();
changeGrid();
}
}
}
//this is the loop. The changeGrid method displays a game grid on a GUI but
// only the 25th iteration is visible on screen. I would like each one to be
// visible for about a half a second before the loop continues.
// I have seen some questions answered on here that are very close to what I'm asking,
// but I just don't really understand how to apply it to my program..
// thanks for any help.
If the code performed by the simulation is quick and does not consume too much CPU and time, then consider using a Swing Timer to do your looping and delay. Otherwise, you'll need to use a background thread such as can be done with a SwingWorker object.
For e.g. if using both Swing Timer and SwingWorker:
private class MultipleGensListener implements ActionListener {
protected static final int MAX_INDEX = 25;
public void actionPerformed(ActionEvent e) {
int timerDelay = 500; // ms delay
new Timer(timerDelay, new ActionListener() {
int index = 0;
public void actionPerformed(ActionEvent e) {
if (index < MAX_INDEX) { // loop only MAX_INDEX times
index++;
// create the SwingWorker and execute it
new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
game.runSimulationOneGen(); // this is done in background thread.
return null;
}
#Override
protected void done() {
changeGrid(); // this is called on EDT after background thread done.
}
}.execute(); // execute the SwingWorker
} else {
((Timer) e.getSource()).stop(); // stop the timer
}
}
}).start(); // start the Swing timer
}
}
NEVER BLOCK THE GUI EVENT THREAD
you can use a timer for that and have it only fire 25 times
final Timer t = new Timer(500,null);
t.addActionListener(new ActionListener(){
int i=0;
public void actionPerformed(ActionEvent e){
game.runSimulationOneGen();//run 1 iteration per tick
changeGrid();
if(i>25){t.stop();}
i++;
}
});
t.setRepeats(true);
t.start();
btw the reason only the last iteration is shown is that gui updates (redraws) are done in a separate event, but to let another event trigger you need to return from the listener method which you didn't
the Timer I showed is a more elaborate iteration which lets other events run in between iterations allowing the gui to show the changes
check my post that shows both methods java.swing.Timer#setDelay(int)
and
correct usage of Thread.sleep(int)
java wait cursor display problem
I have a specific function that I want to be executed after 5 seconds.
How can I do that in Java?
I found javax.swing.timer, but I can't really understand how to use it. It looks like I'm looking for something way simpler then this class provides.
Please add a simple usage example.
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
}
},
5000
);
EDIT:
javadoc says:
After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur.
Something like this:
// When your program starts up
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// then, when you want to schedule a task
Runnable task = ....
executor.schedule(task, 5, TimeUnit.SECONDS);
// and finally, when your program wants to exit
executor.shutdown();
There are various other factory methods on Executor which you can use instead, if you want more threads in the pool.
And remember, it's important to shutdown the executor when you've finished. The shutdown() method will cleanly shut down the thread pool when the last task has completed, and will block until this happens. shutdownNow() will terminate the thread pool immediately.
Example of using javax.swing.Timer
Timer timer = new Timer(3000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// Code to be executed
}
});
timer.setRepeats(false); // Only execute once
timer.start(); // Go go go!
This code will only be executed once, and the execution happens in 3000 ms (3 seconds).
As camickr mentions, you should lookup "How to Use Swing Timers" for a short introduction.
As a variation of #tangens answer: if you can't wait for the garbage collector to clean up your thread, cancel the timer at the end of your run method.
Timer t = new java.util.Timer();
t.schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
// close the thread
t.cancel();
}
},
5000
);
My code is as follows:
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here, and if you have to refresh UI put this code:
runOnUiThread(new Runnable() {
public void run() {
//your code
}
});
}
},
5000
);
Your original question mentions the "Swing Timer". If in fact your question is related to SWing, then you should be using the Swing Timer and NOT the util.Timer.
Read the section from the Swing tutorial on "How to Use Timers" for more information.
you could use the Thread.Sleep() function
Thread.sleep(4000);
myfunction();
Your function will execute after 4 seconds. However this might pause the entire program...
ScheduledThreadPoolExecutor has this ability, but it's quite heavyweight.
Timer also has this ability but opens several thread even if used only once.
Here's a simple implementation with a test (signature close to Android's Handler.postDelayed()):
public class JavaUtil {
public static void postDelayed(final Runnable runnable, final long delayMillis) {
final long requested = System.currentTimeMillis();
new Thread(new Runnable() {
#Override
public void run() {
// The while is just to ignore interruption.
while (true) {
try {
long leftToSleep = requested + delayMillis - System.currentTimeMillis();
if (leftToSleep > 0) {
Thread.sleep(leftToSleep);
}
break;
} catch (InterruptedException ignored) {
}
}
runnable.run();
}
}).start();
}
}
Test:
#Test
public void testRunsOnlyOnce() throws InterruptedException {
long delay = 100;
int num = 0;
final AtomicInteger numAtomic = new AtomicInteger(num);
JavaUtil.postDelayed(new Runnable() {
#Override
public void run() {
numAtomic.incrementAndGet();
}
}, delay);
Assert.assertEquals(num, numAtomic.get());
Thread.sleep(delay + 10);
Assert.assertEquals(num + 1, numAtomic.get());
Thread.sleep(delay * 2);
Assert.assertEquals(num + 1, numAtomic.get());
}
All other unswers require to run your code inside a new thread.
In some simple use cases you may just want to wait a bit and continue execution within the same thread/flow.
Code below demonstrates that technique. Keep in mind this is similar to what java.util.Timer does under the hood but more lightweight.
import java.util.concurrent.TimeUnit;
public class DelaySample {
public static void main(String[] args) {
DelayUtil d = new DelayUtil();
System.out.println("started:"+ new Date());
d.delay(500);
System.out.println("half second after:"+ new Date());
d.delay(1, TimeUnit.MINUTES);
System.out.println("1 minute after:"+ new Date());
}
}
DelayUtil Implementation
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class DelayUtil {
/**
* Delays the current thread execution.
* The thread loses ownership of any monitors.
* Quits immediately if the thread is interrupted
*
* #param duration the time duration in milliseconds
*/
public void delay(final long durationInMillis) {
delay(durationInMillis, TimeUnit.MILLISECONDS);
}
/**
* #param duration the time duration in the given {#code sourceUnit}
* #param unit
*/
public void delay(final long duration, final TimeUnit unit) {
long currentTime = System.currentTimeMillis();
long deadline = currentTime+unit.toMillis(duration);
ReentrantLock lock = new ReentrantLock();
Condition waitCondition = lock.newCondition();
while ((deadline-currentTime)>0) {
try {
lock.lockInterruptibly();
waitCondition.await(deadline-currentTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
} finally {
lock.unlock();
}
currentTime = System.currentTimeMillis();
}
}
}
public static Timer t;
public synchronized void startPollingTimer() {
if (t == null) {
TimerTask task = new TimerTask() {
#Override
public void run() {
//Do your work
}
};
t = new Timer();
t.scheduleAtFixedRate(task, 0, 1000);
}
}
I think in this case :
import javax.swing.*;
import java.awt.event.ActionListener;
is the best. When the Question is prevent Ui stack or a progress not visible before a heavy work or network call. We can use the following methods (from my experience) :
Run a method after one Second :
public static void startMethodAfterOneSeconds(Runnable runnable) {
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
runnable.run();
}
});
timer.setRepeats(false); // Only execute once
timer.start();
}
Run a method after n second once, Non repeating :
public static void startMethodAfterNMilliseconds(Runnable runnable, int milliSeconds) {
Timer timer = new Timer(milliSeconds, new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
runnable.run();
}
});
timer.setRepeats(false); // Only execute once
timer.start();
}
Run a method after n seconds, and repeat :
public static void repeatMethodAfterNMilliseconds(Runnable runnable, int milliSeconds) {
Timer timer = new Timer(milliSeconds, new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
runnable.run();
}
});
timer.setRepeats(true); // Only execute once
timer.start();
}
And the Usage :
startMethodAfterNMilliseconds(new Runnable() {
#Override
public void run() {
// myMethod(); // Your method goes here.
}
}, 1000);
Perhaps the most transparent way is to use the postDelayed function of the Handler class the following way:
new Handler().postDelayed(this::function, 1000);
or you can implement the function inside, for example:
new Handler().postDelayed(() -> System.out.println("A second later"), 1000);
Where the first argument is the function, the second argument is the delay time in milliseconds.
In the first example, the name of the called function is "function".