我想创建一个定期运行某些东西(可运行)但可以在需要时唤醒的类。如果我可以封装整个事情,我想公开以下方法:
public class SomeService implements Runnable {
public run() {
// the code to run at every interval
}
public static void start() { }
public static void wakeup() { }
public static void shutdown() { }
}
不知怎的,我已经走到了这一步。但我不确定这是否是正确的方法。
public class SomeService implements Runnable {
private static SomeService service;
private static Thread thread;
static {
start();
}
private boolean running = true;
private SomeService() {
}
public void run() {
while (running) {
try {
// do what needs to be done
// perhaps peeking at a blocking queue
// or checking for records in a database
// trying to be independent of the communication
System.out.println("what needs to be done");
// wait for 15 seconds or until notify
synchronized (thread) {
try {
thread.wait(15000);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void start() {
System.out.println("start");
service = new SomeService();
thread = new Thread(service);
thread.setDaemon(true);
thread.start();
}
public static void wakeup() {
synchronized (thread) {
thread.notify();
}
}
public static void shutdown() {
synchronized (thread) {
service.running = false;
thread.interrupt();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("shutdown");
}
public static void main(String[] args) throws IOException {
SomeService.wakeup();
System.in.read();
SomeService.wakeup();
System.in.read();
SomeService.shutdown();
}
}
我担心变量应该声明为volatile。并且还担心我应该检查thread.isInterrupted()
的“需要做什么部分”。这看起来是正确的方法吗?我应该把它翻译成遗嘱执行人吗?如何强制在预定执行程序上运行?
修改
在尝试执行者之后,似乎这种方法似乎是合理的。你觉得怎么样?
public class SomeExecutorService implements Runnable {
private static final SomeExecutorService runner
= new SomeExecutorService();
private static final ScheduledExecutorService executor
= Executors.newSingleThreadScheduledExecutor();
// properties
ScheduledFuture<?> scheduled = null;
// constructors
private SomeExecutorService() {
}
// methods
public void schedule(int seconds) {
scheduled = executor.schedule(runner, seconds, TimeUnit.SECONDS);
}
public void force() {
if (scheduled.cancel(false)) {
schedule(0);
}
}
public void run() {
try {
_logger.trace("doing what is needed");
} catch (Exception e) {
_logger.error("unexpected exception", e);
} finally {
schedule(DELAY_SECONDS);
}
}
// static methods
public static void initialize() {
runner.schedule(0);
}
public static void wakeup() {
runner.force();
}
public static void destroy() {
executor.shutdownNow();
}
}
答案 0 :(得分:4)
对于初学者 - 你可能不想自己实现Runnable;你应该接受一个Runnable。如果您希望将类传递给其他人来执行,那么您应该只实现Runnable。
为什么不包装ScheduledExecutorService?这是一个快速(非常差,但应该是功能性的)实现。
public class PokeableService {
private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
private final Runnable codeToRun;
public PokeableService (Runnable toRun, long delay, long interval, TimeUnit units) {
codeToRun = toRun;
service.scheduleAtFixedRate(toRun, delay, interval, units);
}
public void poke () {
service.execute(codeToRun);
}
}
答案 1 :(得分:1)
变量不需要是易失性的,因为它们是在同步块中读取和修改的。
你应该使用一个不同的对象来锁定线程,因为Thread类会自己进行同步。
我建议使用单线程ScheduledExecutorService并删除sleep。然后,如果要在当前睡眠期间运行任务,可以再次将其提交给执行程序一次运行。只需在ExecutorService中使用ScheduledExecutorService扩展的执行或提交方法。
关于检查isInterrupted,你应该这样做,如果do work部分可能需要花费很多时间,可以在中间取消,并且不调用阻塞的方法,并且会以任何方式抛出中断的异常。
答案 2 :(得分:0)
使用wait / notify应该是一种更有效的方法。我也同意这样的建议:使用'volatile'不是必要的,并且在替代对象上进行同步将是明智的,以避免冲突。
其他一些建议:
此代码实现了上述建议。另请注意,只有一个线程执行SomeService执行逻辑,并且它在上次完成之后将发生INTERVAL毫秒。手动触发的wakeUp()调用后,您不应该重复执行。
public class SomeService implements Runnable {
private static final INTERVAL = 15 * 1000;
private Object svcSynchronizer = new Object();
private boolean running = true;
private SomeService() {
}
public void run() {
while (running) {
try {
// do what needs to be done
// perhaps peeking at a blocking queue
// or checking for records in a database
// trying to be independent of the communication
System.out.println("what needs to be done");
// wait for 15 seconds or until notify
try {
svcSynchronizer.wait(INTERVAL);
} catch (InterruptedException e) {
// ignore interruptions
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void wakeUp() {
svcSynchronizer.notifyAll();
}
public void shutdown() {
running = false;
svcSynchronizer.notifyAll();
}
}