我有一个定期执行代码的线程,e。 G。每10秒钟。我想选择以自发的方式调用相同的代码,而不必等待10秒。但是自动和自发执行的代码必须永远不会同时运行,而是如果用户在调用相同的方法时按下执行按钮,它们应该按顺序运行。
有没有人知道一个好的模式,甚至是一个可以满足这种要求的课程?
首先想到的是使工作方法同步。但是在这种情况下,手动执行(例如按下按钮)被阻止,并且必须等到线程中的方法结束。有没有阻止的更好的方法?
示例:
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");
}
}
编辑:我的要求的解决方案:
public class ScheduledExecutor {
public static void main(String[] args) throws InterruptedException {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
executor.scheduleWithFixedDelay(new Work("auto"), 0, 10, TimeUnit.SECONDS);
Thread.sleep(1000);
executor.execute(new Work("manual"));
}
public static class Work implements Runnable {
String text;
public Work(String text) {
this.text = text;
}
@Override
public void run() {
System.out.println("Working " + text + " " + System.currentTimeMillis());
}
}
}
答案 0 :(得分:3)
我有一个定期执行代码的线程,e。 G。每10秒钟。我想选择以自发方式调用相同的代码,而不必等待10秒。
在代码中执行此操作的一种简单方法是,不要使用0x7F
暂停,而是执行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();
}
,因为只有synchronized
线程正在运行它。
注意:正如@shinobi所指出的那样,使用Executor
这样可能会受到虚假唤醒的影响,这可能会在某些操作系统线程实现时发生。
最后,制作Executor
implement Runnable
as opposed to extending Thread
是一种更好的做法。
答案 1 :(得分:3)
我会创建一个新的单线程执行程序服务:
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
只有一个线程可以运行它们。并且,按钮按下处理程序将永远不必等待,因为它只会在队列中放置新对象。
答案 2 :(得分:0)
在服务器线程(执行任务的线程)和客户端线程(需要触发立即执行的线程)之间共享信号量:
Semaphore sem = new Semaphore( 0 );
服务器线程需要执行以下代码(请注意,它是一个无限循环 - 您可能希望将程序终止检查作为while()
的条件插入):
while( true ) {
try {
sem.tryAcquire( 10, TimeUnit.SECONDS );
} catch( InterruptedException e ) {
continue;
}
runTask();
sem.drainPermits();
}
然后,为了触发立即执行,客户端线程需要执行:
sem.release();
因此,只要客户端线程在Semaphore.tryAcquire()
中释放一个(触发立即执行)或超时,就会从信号量获取许可时执行任务(定期执行10s, end-to-start 。)相隔10秒执行 start-to-start 将采用一些稍微复杂的逻辑,并跟踪上次执行的情况。开始时间,但基本想法保持不变。
每次都需要排空许可证,以避免多次背靠背执行任务,如果在执行过程中可能会被触发立即执行。