Java 在周期性运行的线程中自发执行代码
我有一个线程周期性地执行代码,例如。G每10秒。我希望可以选择以一种自发的方式调用相同的代码,而不必等待10秒。但是,自动执行和自发执行的代码决不能同时运行,相反,如果用户在线程调用同一方法时按下execute按钮,它们应该按顺序运行 有人知道一个好的模式或者甚至是一个类可以满足这种需求吗 首先想到的是使工作方法同步。但是在这种情况下,手动执行(例如按钮按下)被阻止,并且必须等待线程中的方法完成。有没有更好的无阻塞方法 例如:Java 在周期性运行的线程中自发执行代码,java,multithreading,Java,Multithreading,我有一个线程周期性地执行代码,例如。G每10秒。我希望可以选择以一种自发的方式调用相同的代码,而不必等待10秒。但是,自动执行和自发执行的代码决不能同时运行,相反,如果用户在线程调用同一方法时按下execute按钮,它们应该按顺序运行 有人知道一个好的模式或者甚至是一个类可以满足这种需求吗 首先想到的是使工作方法同步。但是在这种情况下,手动执行(例如按钮按下)被阻止,并且必须等待线程中的方法完成。有没有更好的无阻塞方法 例如: public class Executor extends Thre
public class Executor extends Thread {
// endless loop, executes work method periodically with pause inbetween
@Override
public void run() {
while( true) {
work( "automatic");
pause(10000);
}
}
// Working method that's executed periodically or manually
private synchronized void work( String text) {
System.out.println( "Working " + text + " " + System.currentTimeMillis());
}
// helper method that pauses the thread
private static void pause( long sleepMs) {
try {
Thread.sleep(sleepMs);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// start automatic execution
Executor executor = new Executor();
executor.start();
// pause a while
pause(1000);
// manual execution
executor.work( "manual");
}
}
编辑:我的需求的解决方案: 我有一个线程周期性地执行代码,例如。G每10秒。我希望可以选择以一种自发的方式调用相同的代码,而不必等待10秒
new Timer(10000, new ActionListener {
public void actionPerformed(ActionEvent evt) {
executorService.execute(() -> doWhatever());
}
}).start();
在代码中执行此操作的一个简单方法不是使用Thread.sleep(…)
暂停,而是执行wait(…)
。然后,每当您希望命令唤醒并手动运行时,它只执行一个notify()
因此,您的代码看起来像:
while( true) {
work( "automatic");
synchronized (this) {
try {
// wait for a bit but allow someone else to awake us to run manually
wait(10000);
} catch (InterruptedException ie) {
// always a good pattern
Thread.currentThread().interrupt();
return;
}
}
}
然后,如果要手动运行,请执行以下操作:
synchronized (executor) {
executor.notify();
}
通知将立即唤醒线程,以便它可以运行其任务。然后,工作方法不需要进行同步
,因为只有执行器
线程在运行它
注意:正如@shinobi所指出的,像这样使用wait()
可能会受到某些OS线程实现中可能发生的虚假唤醒的影响
最后,这是一个更好的实践。我将创建一个新的单线程执行器服务:
ExecutorService executorService = Executors.newFixedThreadPool(1);
然后,我会设置一个计时器,每10秒为executorService
提供一次任务
new Timer(10000, new ActionListener {
public void actionPerformed(ActionEvent evt) {
executorService.execute(() -> doWhatever());
}
}).start();
最后,您可以调用executorService.execute(()->doWhatever())代码>在按钮按下处理程序中,或在代码中的任何其他位置
一次只能运行一个激活的doWhatever()
,因为executorService
只有一个线程可以运行它们。而且,您的按钮按下处理程序永远不必等待,因为它只会将新对象放入队列。在服务器线程(执行任务的线程)和客户端线程(需要触发立即执行的线程)之间共享一个信号量:
服务器线程需要执行以下代码(请注意,这是一个无止境的循环-您可能希望插入程序终止检查作为while()
)的条件):
然后,为了触发立即执行,客户端线程需要执行以下操作:
sem.release();
因此,当客户端线程释放一个许可证(触发立即执行)或在semaphore.tryAcquire()
中超时时(间隔10秒的周期性执行,从结束到开始),服务器线程将在从信号量获取许可证后执行任务让执行从开始到开始相隔10秒将需要一些稍微复杂的逻辑,以及跟踪最后一次执行的开始时间,但基本思想保持不变
为了避免多次背对背执行任务,每次都需要耗尽许可证,在仍在执行的情况下,它可能会被触发立即执行。中断睡眠?如果线程在未处于睡眠模式时被中断,会发生什么情况?能否更新您的问题以明确您希望它如何工作。您的意思是不希望手动和自动线程代码同时运行。然后你说你不想在按下按钮时阻止手动执行…以强调Allan的观点:如果任务在计时器上运行,并且按下了一个按钮,你是1)中止运行,计时器启动的执行,并立即执行按钮启动的任务,还是2)完全跳过按钮启动的任务,还是3)阻止,等待计时器启动的执行完成,然后运行按钮启动的任务?完成。这是选项3,i。E等待线程完成,然后运行手动启动的任务。非常简单,但是没有防止虚假唤醒()的保护。这一点很好。我猜这取决于额外跑步是否是个问题@shinobi。谢谢!这让我找到了正确的方向。使用ScheduledThreadPoolExecutor
并使用scheduleWithFixedDelay
调用工作方法,而不是计时器更符合我的要求。
while( true ) {
try {
sem.tryAcquire( 10, TimeUnit.SECONDS );
} catch( InterruptedException e ) {
continue;
}
runTask();
sem.drainPermits();
}
sem.release();