Safe way to replace AWT EventQueue in running Swing application - java

Various sporadic problems in the Swing application I maintain appear to be caused by the way it replaces the default AWT event queue with its own custom version using Toolkit.getDefaultToolkit().getSystemEventQueue().push(new AEventQueue()). See e.g. Threading and deadlock in Swing application. The problem described there has been resolved, but my tests (using FEST Swing) now tend to run into deadlock.
I suspect the best solution would be to replace the event queue at the beginning of the application initialization, before any Swing components are created. However, there are some dependencies that make that awkward so for the time being I am trying to find a safe way of "pushing" the new event queue after initialization, where it is currently done.
The two approaches I have tried are
push the new queue on the EDT using SwingUtilities.invokeLater();
push the new queue on the main thread after initialization, and after using invokeLater() to avoid deadlock with anything already started on the old EDT.
What I would expect, after reading https://stackoverflow.com/a/8965448/351885, is that the first approach might work in Java 7 but something like the second might be needed in Java 1.6. Indeed the second does work in Java 1.6, while in Java 7 both appear to complete successfully but run very very slowly. This may just be a FEST issue since the application itself seems quite responsive.
So I'm pretty much forced to use the second approach, which at least works in Java 1.6, but I would like to know
- if there is a safer way to implement this, since it seems it might be vulnerable to a race condition if an event appears on the existing queue after invokeLater but before the new queue is created;
- if there is a different approach I should use instead.
More detail
The first "solution" looks like this:
initApplication();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new CustomEventQueue());
}
});
When compiled and run using Java 1.6, I don't understand what it is doing. It seems the thread is waiting for a lock that it already holds:
"AWT-EventQueue-1" prio=10 tid=0x00007f9808001000 nid=0x6628 in Object.wait() [0x00007f986aa72000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d9961cf0> (a atlantis.gui.AEventQueue)
at java.lang.Object.wait(Object.java:502)
at java.awt.EventQueue.getNextEvent(EventQueue.java:490)
- locked <0x00000007d9961cf0> (a atlantis.gui.AEventQueue)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:247)
The second "solution" looks like this:
initApplication();
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
logger.debug("Waiting for AWT event queue to be empty.");
}
});
} catch (InterruptedException e) {
logger.error("Interrupted while waiting for event queue.", e);
} catch (InvocationTargetException e) {
logger.error("Error while waiting for event queue.",e);
}
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new CustomEventQueue());
As stated above, this seems to work OK in Java 1.6 but I'm not convinced it is really safe.
I haven't figured out what is happening when using Java 7, but the main thread seems to spend a long time sleeping the method org.fest.swing.timing.Pause.pause(), which is why I suspect this may be a FEST-specific problem.

Because I can't see reason to reset current EDT with fresh one, my questions are
1) are you got some of
Java deallock, outofmemory ...
RepaintManager exceptions,
2) basically you can
lock current EDT with Thread.sleep(int), with setVisible(false) for caused JComponent,
if is there EDT then you have to use invokeLater, if isn't active then you can choose betweens invokeLater of invokeAndWait
code
if (EventQueue.isDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//some stuff
}
});
} else {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
//some stuff
}
});
} catch (InterruptedException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
}
}
3) notice invokeAndWait must be called out of EDT, othewise caused EDT exceptions with deallock of current EDT
4) if isn't there active EDT, then there isn't reason push() something to the EventQueue
5) simple testing code for all above mentioned ..
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class IsThereEDT {
private ScheduledExecutorService scheduler;
private AccurateScheduledRunnable periodic;
private ScheduledFuture<?> periodicMonitor;
private int taskPeriod = 30;
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
private Date dateRun;
private JFrame frame1 = new JFrame("Frame 1");
public IsThereEDT() {
scheduler = Executors.newSingleThreadScheduledExecutor();
periodic = new AccurateScheduledRunnable() {
private final int ALLOWED_TARDINESS = 200;
private int countRun = 0;
private int countCalled = 0;
private int maxCalled = 10;
#Override
public void run() {
countCalled++;
if (countCalled < maxCalled) {
if (countCalled % 3 == 0) {
/*if (EventQueue.isDispatchThread()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
//some stuff
}
});
} else {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
//some stuff
}
});
} catch (InterruptedException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
}
}*/
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.out.println("Push a new event to EDT");
frame1.repaint();
isThereReallyEDT();
}
});
} else {
if (this.getExecutionTime() < ALLOWED_TARDINESS) {
countRun++;
isThereReallyEDT(); // non on EDT
}
}
} else {
System.out.println("Terminating this madness");
System.exit(0);
}
}
};
periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.SECONDS);
periodic.setThreadMonitor(periodicMonitor);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
isThereReallyEDT();
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.getContentPane().add(new JLabel("Hello in frame 1"));
frame1.pack();
frame1.setLocation(100, 100);
frame1.setVisible(true);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
}
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame2 = new JFrame("Frame 2");
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.getContentPane().add(new JLabel("Hello in frame 2"));
frame2.pack();
frame2.setLocation(200, 200);
frame2.setVisible(true);
isThereReallyEDT();
}
});
}
private void isThereReallyEDT() {
dateRun = new java.util.Date();
System.out.println(" Time at : " + sdf.format(dateRun));
if (EventQueue.isDispatchThread()) {
System.out.println("EventQueue.isDispatchThread");
} else {
System.out.println("There isn't Live EventQueue.isDispatchThread, why any reason for that ");
}
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("SwingUtilities.isEventDispatchThread");
} else {
System.out.println("There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that ");
}
System.out.println();
}
public static void main(String[] args) {
IsThereEDT isdt = new IsThereEDT();
}
}
abstract class AccurateScheduledRunnable implements Runnable {
private ScheduledFuture<?> thisThreadsMonitor;
public void setThreadMonitor(ScheduledFuture<?> monitor) {
this.thisThreadsMonitor = monitor;
}
protected long getExecutionTime() {
long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
return delay;
}
}

Related

Thread not resuming in Java?

I am using a thread to draw images onto a JFrame.
I added a keyListener to listen for the key P, when it is pressed, the drawing of images stops and when I press P again, the drawing should resume.
I tried using wait/ notify along with synchronized block for implementing this.
However only Pausing works, the resume never works.
Strange...
public static void main(String[] args)
{
static JFrame window1 = new JFrame();
static boolean isPaused=false;
Runnable r = new Runnable()
{
public void run()
{
while(true)
{
window1.paintImage();//fn to redraw an image
}
}
};
final Thread t = new Thread(r);
window1.addKeyListener(new KeyListener()
{
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_P)
{
if(isPaused==false)
{
synchronized(t)
{
try
{
t.wait();
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
}
isPaused=true;
} else
{
t.notifyAll();
isPaused=false;
}
}
}
public void keyReleased(KeyEvent arg0){}
public void keyTyped(KeyEvent arg0){}
});
t.start();
}
}
You should go through the Javadoc of Object.wait.
When t.wait() is executed, the current thread is "paused", not t.
More exactly, you are pausing the SWING Thread that is in charge of handling inputs, not the Thread t that you created to redraw the image.
t.wait() makes the SWING Thread to wait until it receive a notify() which will never come because the t.notifyAll() can only be reached by this same SWING Thread (so it is like you are going to sleep and you are waiting for yourself to wake you up... good luck with that).
Here is one solution to fix it (not the best though, since it does not care about synchronization):
final boolean [] pause = new boolean []{false};
Runnable r = new Runnable()
{
public void run()
{
while(true)
{
if(!pause[0])
window1.paintImage();//fn to redraw an image
}
}
};
...
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_P)
{
if(!pause[0])
{
pause[0] = true;
} else
{
pause[0] = false;
}
}
}

Waiting for thread while updating Swing

I have problem with handling threads in my application. It creates JFrame and starts a new Thread. Last one will execute external application and update GUI. Then
I have problem to make Main class to wait for second thread to finish, but also to update GUI simultaneously.
Here's my example (shortened):
class Main {
public int status;
public Main() {
// Creating GUI etc.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JDialog id = new JDialog();
id.button.addMouseListener(new MouseListener()); // Calls generate() method
}
});
}
public void generate() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Make changes to GUI
}
});
GeneratorThread genTest = new GeneratorThread(this, 1, 1, 1);
genTest.start();
//while (status == 0);
System.out.println("Next step.");
}
}
And Thread class:
public class GeneratorThread extends Thread {
protected Main main;
protected int setSize, minValue, maxValue;
public GeneratorThread(Main main, int setSize, int minValue, int maxValue) {
this.main = main;
this.setSize = setSize;
this.minValue = minValue;
this.maxValue = maxValue;
}
public void run() {
// Execute program etc.
// Change GUI from main in the same time
// About 3 seconds
main.status = 1;
}
}
I'm in progress and I wanted to check how it works so far. While worked nice, but it locks Swing somehow and any changes are visible only when GeneratorThread finishes. I would like to update GUI in the real time.
I've tried join(), effects are the same. I also tried wait() (on Main), but then I got IllegalStateMonitorException.
Any hints?
Swing is a single threaded environment. That is, there is a single thread responsible for managing all the interactions and updates to the Swing UI - the Event Dispatching Thread.
Among the golden rules of Swing are...
DON'T block the EDT (Thread.sleep, Thread#join, Object#wait, block IO and/or time consuming tasks (among others) should never be called from within the EDT), doing so will stop the EDT from dispatching events and paint updates (amongst other things)
ONLY create/update Swing UI elements from within the EDT.
This raises a question...how do you "wait" for a thread?
The best way is use an Observer pattern. Basically, you provide the Thread with some kind of reference that it will call to provide notification of events, such as errors and completion...
This will require you to think very carefully about the design of your applications, as you can not rely on a simple A to B execution of your code.
For example...
public class TestThreadCallBack {
public static void main(String[] args) {
new TestThreadCallBack();
}
public TestThreadCallBack() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ThreadCallBack {
public void threadCompleted(Runnable source);
public void threadFailed(Runnable source);
}
public class TestPane extends JPanel implements ThreadCallBack {
private JLabel message;
private JLabel dots;
private int count;
private Timer timer;
public TestPane() {
setLayout(new GridBagLayout());
message = new JLabel("Running background task, please wait");
dots = new JLabel(" ");
add(message);
add(dots);
timer = new Timer(250, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (count > 3) {
count = 0;
}
StringBuilder sb = new StringBuilder(3);
for (int index = 0; index < count; index++) {
sb.append(".");
}
for (int index = count; index < 3; index++) {
sb.append(" ");
}
dots.setText(sb.toString());
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
Thread thread = new Thread(new BackgroundTask(this));
thread.start();
}
#Override
public void threadCompleted(Runnable source) {
timer.stop();
message.setText("Task completed successfully");
}
#Override
public void threadFailed(Runnable source) {
timer.stop();
message.setText("Task failed");
}
}
public class BackgroundTask implements Runnable {
private ThreadCallBack callBack;
public BackgroundTask(ThreadCallBack callBack) {
this.callBack = callBack;
}
#Override
public void run() {
System.out.println("Background task underway...");
try {
Thread.sleep(2000);
} catch (InterruptedException interruptedException) {
}
int result = (int) Math.round((Math.random() * 1));
if (result == 0) {
callBack.threadCompleted(this);
} else {
callBack.threadFailed(this);
}
}
}
}
Updating the UI from within a Thread other then the EDT is, well, messy. An easier solution would actually be to use a SwingWorker. This has publish/process methods that make easy to update the UI and progress methods that can be used to provide feedback about the progress of the current task.
You can use it's done method to notify interested parties when the worker has completed.
Update your GUI from within the thread using SwingUtilitied.invokeLater or, alternatively, synchronise the main variable!
http://www.vogella.com/articles/JavaConcurrency/article.html#concurrencyjava
Maybe it already suffices to make "status" volatile?

Java Swing Concurrency display JTextArea

I need to execute/display a series of events from a Arraylist to a JTextArea, however, each Event gets execute with different time. Following is a quick example of my goal:
public void start(ActionEvent e)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
jTextArea.append("Test" + "\n");
try
{
Thread.sleep(3000);
} catch (InterruptedException e1)
{
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
}
So right now, "Test" and "Test1" display on JTextArea after whole execution is completed.
How do I make "Test" display first, then 3 secs later, display "Test1"
Thank u all in advance
invokeLater schedules the runnable to run on the Event Dispatch Thread. You shouldn't sleep within it or you will starve the dispatch thread. Try using a separate worker thread instead:
Thread worker = new Thread(new Runnable(){
public void run(){
jTextArea.append("Test" + "\n");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
jTextArea.append("Test1" + "\n");
}
});
worker.start();
If your tasks are time/cpu intensive, then yes, definitely use a background thread to do this such as a SwingWorker object or a Runnable run in a Thread. If however what you need to do is to stagger the display of something and all you are looking for is the Swing equivalent of Thread.sleep(3000), then your best option is to use a Swing Timer. There is an excellent tutorial on how to use these which you can find here: http://download.oracle.com/javase/tutorial/uiswing/misc/timer.html
For example:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Fu extends JPanel {
private static final int TIMER_DELAY = 600;
protected static final int MAX_COUNT = 20;
private JTextArea jTextArea = new JTextArea(10, 10);
private JButton startBtn = new JButton("Start");
private Timer timer;
public Fu() {
startBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
startAction(e);
}
});
add(new JScrollPane(jTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED));
add(startBtn);
}
private void startAction(ActionEvent e) {
if (timer != null && timer.isRunning()) {
// prevent multiple instances of timer from running at same time
return;
}
timer = new Timer(TIMER_DELAY, new ActionListener() {
private int count = 0;
public void actionPerformed(ActionEvent e) {
if (count < MAX_COUNT) {
count++;
jTextArea.append("Test " + count + "\n");
} else {
jTextArea.append("Done! \n");
timer.stop();
timer = null;
}
}
});
timer.setInitialDelay(0);
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Foo");
frame.getContentPane().add(new Fu());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
As pointed out, this is a bad idea, as you will block the event thread.
However, understanding the reason for this is important as well. As you seem to know, all code that affects the state of Swing components needs to happen in the event handling thread (which is the reason why invokeLater and friends should always be used).
What is a bit less better known is that paining code also executes in the event handling thread. When your call to Thread.sleep is executing, it's not only blocking the event thread, it's also blocking any painting of components. This is why the full update appears to happen in one go -- the JTextArea is updated but it can't be repainted until your run method returns.
Lots of info available here: http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html

Issue with Java join() method

First of all here are some code snippets:
public void startThread() {
this.animationThread = new Thread(this);
this.animationThread.start();
try {
this.animationThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void run() {
pirateMainAnimation.animate();
}
public void animate() {
for (int j = 0; j < 9; j++) {
try {
Thread.sleep(250);
} catch (InterruptedException e) {
break;
}
PirateAnimationPanel.getInstance().setCurrent(j);
PirateAnimationPanel.getInstance().repaint();
}
}
I'm trying to animate some images. The thing is that I want the main thread to wait for the animation thread to finish and then to continue. I searched around, read a little bit and decided to use the join() method. It perfectly waits for the thread to finish but I doesn't animate correctly. The repaint() method gets called 2 times instead of nine. I think maybe the problem is because I used singletons. Here is the singleton implementation.
import java.awt.Graphics;
import java.awt.MediaTracker;
import javax.swing.JPanel;
import uk.ac.aber.dcs.piratehangman.animation.PirateMainAnimation;
import uk.ac.aber.dcs.piratehangman.utilityclasses.AnimationThread;
#SuppressWarnings("serial")
public class PirateAnimationPanel extends JPanel {
private int current;
private MediaTracker mTracker;
private PirateMainAnimation pirateMainAnimation;
private AnimationThread animationThread;
private PirateAnimationPanel() {
this.current = 0;
this.pirateMainAnimation = new PirateMainAnimation();
mTracker = new MediaTracker(this);
this.animationThread = new AnimationThread();
setMediaTracker();
repaint();
}
private void setMediaTracker() {
for (int i = 0; i < 9; i++) {
mTracker.addImage(
this.pirateMainAnimation.getImagesForAnimation()[i],
this.pirateMainAnimation.getImagesForAnimationID()[i]);
try {
mTracker.waitForID(this.pirateMainAnimation
.getImagesForAnimationID()[i]);
} catch (InterruptedException e) {
System.out.println("Error loading image: " + i);
}
}
}
public void playAnimation() {
this.animationThread.startThread();
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
System.out.println("called");
g.drawImage(this.pirateMainAnimation.getImagesForAnimation()[current],
0, 0, this);
}
private static class PirateAnimationPanelHolder {
private static final PirateAnimationPanel pirateAnimationPanel =
new PirateAnimationPanel();
};
public static PirateAnimationPanel getInstance() {
return PirateAnimationPanelHolder.pirateAnimationPanel;
}
public void setCurrent(int current) {
this.current = current;
}
public int getCurrent() {
return current;
}
}
I think you mean that the paintComponent() methods only gets called twice. Also I think you should be able to remove the call to super.paintComponents() if you fill the component to the background color.
The repaint() method only marks the component as dirty and requests a re-render on the next paint.
I would have expected the Swing thread to be able to repaint within the 250ms but I'm not sure what other work is being done/rendered. You might want to put a call to MediaTracker.waitForAll() before the animation.
While the static singleton is not adding much I don't think it is causing a problem (in this case).
Update:
So the problem is that the join() is on the Swing event Thread which is blocking the repainting of the component. I suggested a call like the following to show the "new game dialog after the last animation:
SwingUtilities.invokeLater(new Runnable() {
public void run() { showDialog(); }
})
"Note that events being posted to the EventQueue can be coalesced," which may explain the disparity. Also, be certain to build your GUI on event dispatch thread. See A More Complex Image Icon Example for a more detailed discussion.
Addendum: Improving Perceived Performance When Loading Image Icons has a nice SwingWorker example that may simplify the off-loading.

Run a java function after a specific number of seconds

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".

Categories

Resources