唤醒睡眠线程 – 中断()与将睡眠“分裂”为多个睡眠

这个要求出现在我的Android应用程序中,但它通常适用于Java。 我的应用程序每隔几秒就“做一些事情”。 我已经实现了如下(只是相关的片段 – 不是完整的代码):

Snippet1:

public class PeriodicTask { private boolean running = true; private int interval = 5; public void startTask(){ while (running){ doSomething(); try{ Thread.sleep(interval * 1000); } catch(InterruptedException e){ //Handle the exception. } } } public void stopTask(){ this.running = false; } public void setInterval(int newInterval){ this.interval = newInterval; } } 

正如您所看到的,这种方法的问题是setInterval()不会立即生效。 它仅在前一个sleep()完成后生效。

由于我的用例允许最终用户以固定步长(1秒 – 从1到60秒)设置间隔,因此我将实现修改为在循环内hibernate; 并按如下方式每秒检查新的间隔值:

Snippet2:

 public class PeriodicTask { private boolean running = true; private int interval = 5; private int loopCounter = 0; public void startTask(){ while (running){ doSomething(); try{ while(loopCounter < interval) { Thread.sleep(1 * 1000); loopCounter ++; } } catch(InterruptedException e){ //Handle the exception. } } } public void stopTask(){ this.running = false; } public void setInterval(int newInterval){ synchronized (this) { this.interval = newInterval; if(newInterval < loopCounter){ loopCounter = 0; } } } } 

有没有理由不使用这种方法?

我最近遇到了为此目的的interrupt()方法。 但是,我无法弄清楚如何使用它。 首先,与sleep方法不同,中断方法不是static 。 那么,我打断什么Thread呢?

 public void setInterval(int newInterval){ this.interval = newInterval; //What thread do I call interrupt() on? } 

其次,如果我成功中断了正在hibernate的Thread ,我相信将会执行InterruptedExceptioncatch块。 但是,此时我需要再次调用startTask() 。 关于此递归的终止,我很困惑。 关于trunk()的使用,我已经经历了几个关于SO的问题,但是找不到任何可以帮助我的东西。

有什么指针吗?


编辑 – 有关确切要求的更多详细信息:

我的应用程序每隔几秒钟使用REST调用获取一些值。 更新间隔可由用户配置。

现在,假设更新间隔已设置为60秒。 我发布的Snippet1工作(错误)如下:

  • 线程进入睡眠状态60秒。
  • 现在,假设用户将更新间隔更改为5秒。 线程还在睡觉。
  • PeriodicTask仅在60秒过期后才会看到新的更新间隔。

确切的要求是新的更新间隔应该立即生效(或者至少在设置后不迟于1秒 – 因为这是用户可能感知的东西)。

我的Snippet2和Snippet3试图达到这个要求。

IIRC,在Java中你可以使用超时来对象。 这不是你想要的吗? 如果要从另一个线程更改超时,请更改一些’waitValue’variables并通知()。 然后线程将“立即”运行,然后再次使用新的超时值。 无需明确睡眠。

这个答案帮助我完成了这项工作。 发布一些关于我如何实现它的代码。 特别重要的是startTask()setInterval()

 public class PeriodicTask { private volatile boolean running = true; private volatile int interval = 5; private final Object lockObj = new Object(); public void startTask() { while (running) { doSomething(); synchronized (lockObj) { try{ lockObj.wait(interval * 1000); } catch(InterruptedException e){ //Handle Exception } } } } public void stopTask() { this.running = false; } public void setInterval(int newInterval) { synchronized (lockObj) { this.interval = newInterval; lockObj.notify(); } } } 

我不清楚你真正想做什么。 你的目标是停止在PeriodicTask中运行循环的线程,还是只想打破循环并允许线程继续? 如果您只想打破循环但允许线程继续,请考虑以下示例:

 public class ThreadStopExample { public static void main ( String[] args ) throws InterruptedException { final PeriodicTask task = new PeriodicTask (); Thread t = new Thread ( new Runnable () { @Override public void run () { System.out.println ( Thread.currentThread ().getName () + " starting" ); task.startTask (); System.out.println ( Thread.currentThread ().getName () + " done with the periodic task" ); } } ); t.start (); Thread.sleep ( 12000 ); task.setInterval ( 1 ); Thread.sleep ( 3000 ); task.stopTask (); } static class PeriodicTask { private volatile boolean running = true; private volatile int interval = 5; public void startTask () { running = true; while ( running ) { doSomething (); try { int count = 0; while ( running && count++ < interval ) { Thread.sleep ( 1000 ); } } catch ( InterruptedException e ) { Thread.currentThread ().interrupt (); running = false; break; } } } public void stopTask () { running = false; } public void setInterval ( int newInterval ) { interval = newInterval; } private void doSomething () { System.out.println ( "[" + Thread.currentThread ().getName () + "] Interval: " + interval ); } } } 

这与您现有的代码非常相似。 请注意volatile字段,以确保运行PeriodicTask循环的线程与尝试更改间隔和停止任务的主线程之间的正确同步(请参阅此处获取有关java内存模型的更多信息的链接)。 如您所见,在调用停止任务之后,继续使用PeriodicTask实例的线程。 另请注意,PeriodicTask在收到中断的exception时会调用当前线程的中断。 这确保了在当前线程上设置了中断标志,以便任何外部代码都能够看到中断并做出适当的反应,例如,不是打印完成,运行PeriodicTask的线程可能已经检查了自身的中断状态并做了一些有趣的事情。

如果您的目标是停止线程本身,那么您可能希望使用PeriodicTask扩展Thread,除非您有充分的理由这样做,否则不推荐使用,或者让PeriodicTask实现Runnable。 考虑下一个例子:

 public class ThreadStopExample2 { public static void main ( String[] args ) throws InterruptedException { final PeriodicTask task = new PeriodicTask (); Thread t = new Thread ( task ); t.start (); Thread.sleep ( 12000 ); task.setInterval ( 1 ); Thread.sleep ( 3000 ); t.interrupt (); } static class PeriodicTask implements Runnable { private volatile int interval = 5; @Override public void run () { while ( true ) { doSomething (); try { int count = 0; while ( count++ < interval ) { Thread.sleep ( 1000 ); } } catch ( InterruptedException e ) { Thread.currentThread ().interrupt (); break; } } } public void setInterval ( int newInterval ) { interval = newInterval; } private void doSomething () { System.out.println ( "[" + Thread.currentThread ().getName () + "] Interval: " + interval ); } } } 

这里,PeriodicTask处于一个繁忙的循环中,直到运行它的线程被中断。 中断用于指示PeriodicTask退出循环,然后允许线程在run方法结束时完成。

关于你的两个明确的问题:不,如果你不打算控制执行线程,我没有看到使用PeriodicTask的任何实际问题,例如,可能是池中的线程运行PeriodicTask的实例(但确保修复您的代码以便正确同步),并且,在使用中断时,您可以在要中断的线程的实例上调用它。 如何获得对该线程的引用取决于您的系统。

你在正在hibernate的线程上调用interrupt() ,它会抛出你在catch块中处理的InterruptedException 。 然后你有了新的间隔,你可以循环然后回去睡觉。 如果你捕获并处理InterruptedException不会发生任何进一步的事情。

让我提供一些创建和中断线程的示例链接,从您的评论中您似乎缺少一些重要的想法。 请仔细阅读这些内容,然后您应该了解执行所要求的标准方法。

http://docs.oracle.com/javase/tutorial/essential/concurrency/simple.html http://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html