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);
}
});
}
});
}
}
Related
I am working on this application in swing .It is actually a voice controlled thingy...I give voice commands and some action is performed.But the thing is that once it is deployed, it is on an infinite while loop, it continuously searches for voice( which it should..imagine the jarvis of iron man movie) .. but this while loop freezes up my gui.I can not update it.can not hide panels , can not play sound.
swing worker and swing utilities shouldn;t help me because they check for the code after certain period of time while i need real time voice recognition..
So what can be done ? Can i make my gui interact with another java program? Like the java prog will do the voice recognition and pass on the reply to the gui?
Here is the code sketch
class{
main(){
new class()
}
class(){
frames + content pane initialized
mousePresssed()
{
///the while loop starts here and looks for voice commands..any gui update code doesnt work here..while it detects the voice fine..continuously.
}
}
Basically, you need to have your infinite loop run in another Thread than the EDT. And whenever you want to update your GUI, do it on the EDT, using a SwingUtilities.invokeLater call. The delay for calling the update of the GUI in invokeLater will be barely noticeable. SwingUtilities.invokeLater is not based on a polling mechanism. The only thing it does is transform a Runnable into an event which is then posted on the EDT. The EDT will then execute your Runnable as soon as possible, so most of the time, instantly.
Now for the pattern on how to communicate between your Thread and your GUI, you can simply use the "Observer" pattern. Your voice recognition thread is somehow a model and your UI simply listens for changes on that model. Whenever the model changes, the UI updates itself.
I made a dummy example of such thing. For the "Observer" pattern, I used the PropertyChangeSupport for it.
For the model, I created a dummy thread which generates random "command" every once in a while and the UI updates itself accordingly:
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class TestThreadingAndGUI implements PropertyChangeListener {
private JFrame frame;
private JLabel label;
private DummyRunnable runnable;
public static class DummyRunnable implements Runnable {
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private String command;
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
#Override
public void run() {
Random random = new Random();
while (true) {
try {
Thread.sleep(((random.nextInt(3)) + 1) * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 15; i++) {
sb.append((char) ('a' + random.nextInt(26)));
}
setCommand(sb.toString());
}
}
public String getCommand() {
return command;
}
private void setCommand(String command) {
String old = this.command;
this.command = command;
pcs.firePropertyChange("command", old, command);
}
}
protected void initUI(DummyRunnable runnable) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
frame.add(label);
frame.setSize(600, 600);
frame.setVisible(true);
this.runnable = runnable;
runnable.addPropertyChangeListener(this);
}
private void executeCommand() {
label.setText(runnable.getCommand());
}
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("command")) {
// Received new command (outside EDT)
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Updating GUI inside EDT
executeCommand();
}
});
}
}
public static void main(String[] args) {
final DummyRunnable runnable = new DummyRunnable();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
TestThreadingAndGUI testThreadingAndGUI = new TestThreadingAndGUI();
testThreadingAndGUI.initUI(runnable);
}
});
new Thread(runnable).start();
}
}
I am making a program to check the stock market for a symbol and I got that far, and added a basic gui to it. I am stumped on how to make it check every hour and create a green up arrow if it increased and red down arrow if it decreased.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class QuoteTracker {
JFrame frame;
JPanel mainPanel;
JLabel enterLabel;
JLabel resultLabel;
JTextField text;
JTextField result;
JButton query;
JButton redArrow;
JButton greenArrow;
String url;
public static void main(String[] args) {
new QuoteTracker().buildGui();
}
public class checkingQuote implements Runnable {
#Override
public void run() {
while (true) {
try {
checkQuote(url);
//if increase in value green button
System.out.println("Sleeping");
Thread.sleep(1000 * 60 * 60);
System.out.println("Waking");
} catch (InterruptedException ie) {
ie.printStackTrace();
break;
}
}
}
}
public void checkQuote(String symbol) {
try {
String url = "http://finance.yahoo.com/q?s=" + symbol + "&ql=0";
this.url = url;
Document doc = Jsoup.connect(url).get();
Elements css = doc.select("p > span:first-child > span");
result.setText(css.text());
} catch (IOException e) {
}
}
public void buildGui() {
frame = new JFrame("QuoteTracker");
mainPanel = new JPanel();
enterLabel = new JLabel("enter symbol ");
resultLabel = new JLabel("result ");
text = new JTextField(4);
result = new JTextField(8);
query = new JButton("query");
query.addActionListener(new queryListener());
mainPanel.add(enterLabel);
mainPanel.add(text);
mainPanel.add(query);
mainPanel.add(resultLabel);
mainPanel.add(result);
frame.getContentPane().add(mainPanel);
frame.setSize(300, 400);
frame.setVisible(true);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
class queryListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ev) {
checkQuote(text.getText());
}
}
}
Do I even need a thread? I've never made one before and tried to add things that made sense. I am thinking I either need a thread or to use java's Timer?
Use SwingWorker to execute long running task in the background while updating the UI based on some results from that long running task. That means, it is actually about two different threads communicating to each other - Worker Threads and Event Dispatch Thread (EDT)
But before that, I want to point some few notes about your code.
Invoke the initialization of your UI in the EDT. That is, instead of just straightly calling new QuoteTracker().buildGui(), call it inside the run method of a Runnable passed to SwingUtilities.invokeLater (like this)
Classes should start in capital letter as per the Java standard.
To apply SwingWorker in you existing code, you can do the following :
First, you must place your checkQuote method in some other class (ideally a service class) then modify your checkQuote method to return the String that is set to the textfield result. Something like this
public Class QuoteService{
public String checkQuote(String symbol) {
try {
String url = "http://finance.yahoo.com/q?s=" + symbol + "&ql=0";
this.url = url;
Document doc = Jsoup.connect(url).get();
Elements css = doc.select("p > span:first-child > span");
return css.text();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
You can then make your QuoteTracker class to focus mainly in the UI part of your application. Just create the service object as instance level field so that you can freely call checkQuote method within your the class.
Invoke SwingWorker when the button is clicked.
public void actionPerformed(ActionEvent ev) {
new SwingWorker<Void, String>() {
#Override // this method is done in the Worker Thread
protected Void doInBackground() throws Exception {
while(true){
String res = checkQuote(text.getText());
publish(res); //will call the process method
Thread.sleep(1000 * 60 * 60); //1 hour
}
}
#Override // this method is done in the EDT
protected void process(List<String> resultList){
String res = resultList.get(0);
if(!"".equals(res)){
result.setText(res);
}
}
#Override // this method is done in the EDT. Executed after executing the doInBackground() method
protected void done() {
//... clean up
}
}.execute();
}
Note that done() will be executed after the execution of doInBackground() is finished, which means, in the code I posted, it will never be executed since the while loop used to periodically call checkQuote is infinite. Just modify it so that you can interrupt that loop according to your need
Further Read : Concurrency in Swing
You can use thread and normal while loop in main thread as well, but at the very first , you need to start you thread and that thread must refer your object.
Add following line in public void buildGui() {
Thread t1 = new Thread(new checkingQuote());
t1.start();
This will start you thread, for testing purpose i have modified checkingQuote class
public class checkingQuote implements Runnable {
int count = 0;
#Override
public void run() {
System.out.println("Inside Runner");
while (true) {
try {
count++;
checkQuote(url);
//if increase in value green button
result.setText(""+count);
System.out.println("Sleeping");
Thread.sleep(1000);
System.out.println("Waking");
} catch (InterruptedException ie) {
ie.printStackTrace();
break;
}
}
}
}
I am seeing number change in the text box.... same way you can alter the logic to get and show the quotes.. but you must keep the value for previous quote to compare with the latest code to show green and red notification...
In gui application it is better to use Timer, also you may use ScheduledThreadPoolExecutor. But in the second case notice, that your scheduled tasks may run in non-GUI thread. As you can't invoke ATW/Swing directly from another thread, you should wrap any call to Swing into SwingUtilities.invokeLater() method.
Also notice, that when you do something durable inside GUI thread, the whole GUI becomes unrepsonsive. So, to achieve a better responsiveness, you would query in a separate thread, and expose results to Swing through invokeLater after quotes have checked. So your checkQuote method may be rewritten this way:
public void checkQuote(String symbol) {
try {
final String url = "http://finance.yahoo.com/q?s=" + symbol + "&ql=0";
Document doc = Jsoup.connect(url).get();
Elements css = doc.select("p > span:first-child > span");
final String text = css.text();
SwingUtilities.invokeLater(new Runnable() {
#Override public void run() {
this.url = url;
result.setText(text);
}
}
} catch (IOException e) {
// Don't swallow exceptions
logger.error("Something gone wrong", e);
}
}
public void checkQuote() {
final String symbol = text.getText();
new Thread(new Runnable() {
#Override public void run() {
checkQuote(symbol);
}
}).start();
}
and call it from Timer and from button click listener.
Anyone have any idea how to make check box do a task until it gets unchecked?
JCheckBox chckbxInspecAuto = new JCheckBox("Inspe. Auto.");
chckbxInspecAuto.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0)
{
try {
gimage = vision.grab();
cvSaveImage("image001.bmp", gimage);
ipanel.loadImage("image001.bmp");
} catch (com.googlecode.javacv.FrameGrabber.Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
EDIT: The code is over there now...
You could extend the SwingWorker class or even Thread to implement the functionality you want the checkbox to control. Create methods contiueWorking() and stopWorking() and in the run method check some boolean flag to see whether to do the task or sleep.
As Robin commented on a post below, swing components should only be accessed from the Event Worker Thread. I violated this rule in my example below because I wanted to produce something simple and since the running thread spends most of its time sleeping, it works fine.
I would use SwingWorker for a more time consuming task that I wish to update the user on as it executes. For instance, say we have a thread generate and then send a set of email messages, and for each e-mail, display some text in the UI that confirms the message was sent or indicates why it was undeliverable. Each message may lock the Thread up for a while. The UI will not be updated in sync with the Thread's execution of the e-mail task. SwingWorker to the rescue. The java2s.com site has a SwingWokerDemo.
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.event.*;
import java.util.ArrayList;
/**
* The sequence of prime integers is calculated and displayed on the screen.
* This little program demonstrates how a checkbox can be used
* to start and stop a thread.
*/
public class ThreadDemo extends JFrame {
JCheckBox runningCB;
JTextArea displayArea;
JTextField delayField;
PrimeCalcThread workerThread = null;
public ThreadDemo() {
super("Prime Numbers");
runningCB = new JCheckBox("Calculate Primes");
JPanel topPanel = new JPanel();
topPanel.add(runningCB);
topPanel.add(new JLabel("Dealy: "));
delayField = new JTextField(10);
delayField.setText("500");
topPanel.add(delayField);
getContentPane().add(topPanel,BorderLayout.NORTH);
displayArea = new JTextArea(30,80);
displayArea.setText("2, 3, 5, ");
displayArea.setLineWrap(true);
JScrollPane scroller = new JScrollPane(displayArea);
getContentPane().add(scroller,BorderLayout.CENTER);
pack();
runningCB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(runningCB.isSelected() ) {
if(workerThread != null)
workerThread.contiueWorking();
else {
workerThread = new PrimeCalcThread();
workerThread.start();
}
}
else {
if(workerThread != null)
workerThread.stopWorking();
}
}
});
delayField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String input = delayField.getText().trim();
try {
int d = Integer.parseInt(input);
workerThread.setDelay(d);
}
catch(NumberFormatException nfe) { }
}
});
setVisible(true);
}
public static void main(String[] arg) {
ThreadDemo window = new ThreadDemo();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* When this thread is active it calculates prime numbers. The Thread
* continues until it is paused with stopWorking() or until the
* boolean field endThread is set to true.
*/
class PrimeCalcThread extends Thread {
ArrayList<Integer> primeList;
int delay = 500;
boolean active = true;
boolean endThread = false;
private int lastPrime;
public PrimeCalcThread() {
primeList = new ArrayList<> (1024);
primeList.add(2); primeList.add(3); primeList.add(5);
lastPrime=5;
}
public void run() {
while(!endThread) {
if(active) {
calcNextPrime();
displayArea.append(lastPrime + ", ");
}
try { sleep(delay); }
catch(InterruptedException whatever) { }
}
}
private void calcNextPrime() {
int p = lastPrime+2;
while(!isPrime(p))
p+=2;
primeList.add(p);
lastPrime = p;
}
/**
* Checks if p is prime by checking for divisibility by all primes in the
* calculated primeList so far. This method only works if sqrt(p) < lastPrime
*/
private boolean isPrime(int p) {
int maxCheck = (int) Math.sqrt(p) + 1;
for(int prime: primeList) {
if(p % prime == 0)
return false;
if(prime > maxCheck)
break;
}
return true;
}
public int getLastPrime() {
return lastPrime;
}
public ArrayList<Integer> getPrimeList() {
return primeList;
}
public int getDelay() {
return delay;
}
public void setDelay(int waitTime) {
if(waitTime>=0)
delay = waitTime;
}
public void contiueWorking() {
active=true;
}
public void stopWorking() {
active=false;
}
}
}
- Well create a Daemon Thread and run it in an infinite loop, let it keep checking whether the JCheckBox is checked or not using isSelected() method or ItemListener.
- And when the JCheckBox gets selected then create a Thread to execute the task, or use SwingWorker Class which properly synchronizes the UI and Non-UI thread's work.
It depends on the task.
If a task is on other process on other machine you could simply send the right messages.
If the task is a separated thread in the same application you could do what wxyz suggests or you could use some kind of Listener with the ability of start and stop the Thread (the thread is always the same or you are creating a new one every time you select the checkbox?).
My favorite one would be to use Observer pattern in the "PUSH" way, so you would use some kind of external signal to the thread which when sent would create and/or stop it.
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.
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".