在特定秒数之后运行Java函数

192

我有一个特定的函数,希望在5秒后执行。如何在Java中实现这个功能?

我找到了javax.swing.timer,但是我无法理解如何使用它。看起来我正在寻找比这个类提供的更简单的东西。

请添加一个简单的使用示例。


你想要等待5秒钟然后执行某些操作,还是在这5秒钟内继续做其他事情? - whiskeysierra
我想要继续做其他事情 - ufk
12个回答

292
new java.util.Timer().schedule( 
        new java.util.TimerTask() {
            @Override
            public void run() {
                // your code here
            }
        }, 
        5000 
);

编辑:

javadoc指出:

在最后一个对 Timer 对象的活动引用消失并且所有未完成的任务都已执行完成之后,计时器的任务执行线程会优雅地终止(并成为垃圾回收的对象)。但是,这可能需要任意长的时间才能发生。


2
如果你运行那段代码,将会导致线程泄漏。确保在完成后清理定时器。 - skaffman
1
@skaffman:我从javadoc中添加了一条语句。调用schedule后你真的需要清理吗? - tangens
1
可能没问题,但也有可能会出问题。如果你多次运行这段代码片段,就会有松散的线程四处乱跑,而没有任何整理它们的方法。 - skaffman
5
import java.util.Timer; import java.util.TimerTask; 可能会更明显地表明这不是 javax.swing.Timer。需要注意的是,如果你正在使用 Swing(实际上也包括 AWT),你不应该在非事件分派线程(EDT)上改变组件(java.util.Timer 的任务不好;javax.swing.Timer 的操作则可行)。 - Tom Hawtin - tackline
2
根据文档,调用计时器的cancel方法会在运行方法结束时清除TimerTasks的执行线程。 - Dandalf
2
2021年这里问一下,您是否仍需要手动清除线程? - Georodin

75

类似这样:

// 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();

如果您需要更多线程池中的线程,可以使用Executor上的其他各种工厂方法。

请记住,在完成后关闭执行器非常重要。 shutdown()方法将在最后一个任务完成时清除关闭线程池,并阻塞直到完成此操作。 shutdownNow()将立即终止线程池。


1
shutdown() 不会阻塞。而 awaitTermination() 会。 - Yonas
shutdown() 只是不允许提交新任务。 shutdownNow() 还会中断任务(不一定立即终止)。 - dan1st

33

使用 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!

这段代码只会执行一次,在3秒(3000毫秒)后执行。

正如camickr所提到的,您应该查阅 "如何使用Swing Timer" 来进行简短的介绍。


加油,加油,加油! :-) - Farshid Ahmadi
运行得非常好!我认为这是最有效的方法。 - Noor Hossain
对我没用 - Sayed Hussainullah Sadat

20
作为@tangens答案的变化:如果你等不及垃圾收集器来清理你的线程,可以在run方法结束时取消定时器。
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 
);

Timer t 应该声明为 final 吗?因为它被内部类访问了。 - Joshua Pinter
1
@JoshuaPinter 是的,它应该被声明为final,但至少在Java 8中不需要显式地声明为final。它只需要是“有效地final”(https://javarevisited.blogspot.com/2015/03/what-is-effectively-final-variable-of.html)。 - Dandalf

9

我的代码如下:

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 
);

7

您原始的问题提到了“Swing Timer”。如果您的问题确实与Swing有关,则应使用Swing Timer而不是util.Timer。

请阅读Swing教程中关于“如何使用计时器”的部分以获取更多信息。


5
你可以使用 Thread.Sleep() 函数。
Thread.sleep(4000);
myfunction();

您的函数将在4秒后执行。然而,这可能会暂停整个程序...


它只保证执行将在4秒后运行,这可能意味着也可能在10秒后运行! - questzen
2
questzen,你会发现所有这里的方法都是如此。实际上,即使在操作系统级别安排某些事情,通常也只能保证事件发生前的最短经过时间。 - Ethan
这不是实际问题的内容。 - Inder R Singh
我刚刚不得不给这个回答点个踩 - 它根本不是对问题的回答。 - theMayer
OP在评论中说:“我想继续做其他事情”;这段代码显然不行。 - Abhijit Sarkar

5

ScheduledThreadPoolExecutor具有此功能,但它相当笨重。

Timer也具有此功能,但即使仅使用一次也会打开多个线程。

以下是一个简单的实现和测试(签名接近Android的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
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());
}

循环中调用了 sleep 函数,会产生警告。 - shareef
while 只是为了忽略中断。 - AlikElzin-kilaka

4

所有其他答案都需要在新线程中运行您的代码。在某些简单的用例中,您可能只想等一会儿,然后在同一个线程/流中继续执行。

下面的代码演示了这种技术。请记住,这类似于java.util.Timer在内部执行的操作,但更加轻量级。

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实现

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();
        }
    }
}

3
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);
        }
    }

3
虽然这段代码可能回答了问题,但提供有关代码为何以及如何回答问题的额外上下文信息可以提高其长期价值。 - Mateus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接