这个要求出现在我的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秒)设置间隔,因此我将实现修改为在循环内休眠;并按如下方式每秒检查新的间隔值:
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()
方法。但是,我无法弄清楚如何使用它。例如,与睡眠方法不同,中断方法不是static
。那么,我会中断Thread
什么?
public void setInterval(int newInterval){
this.interval = newInterval;
//What thread do I call interrupt() on?
}
其次,如果我成功中断了休眠Thread
,我相信catch
的{{1}}块将被执行。但是,此时我需要再次调用InterruptedException
。关于这个递归的终止,我很困惑。关于中断()的使用,我已经经历了几个关于SO的问题,但无法弄清楚有什么能帮到我。
任何指针?
我的应用每隔几秒钟使用一次REST调用获取一些值。更新间隔可由用户配置。
现在,请说更新间隔已设置为60秒。我发布的Snippet1工作(错误)如下:
startTask()
才能看到新的更新间隔。确切的要求是新的更新间隔应该立即生效(或者至少在设置后不迟于1秒 - 因为这是用户可能会感知到的)。
我的Snippet2和Snippet3试图达到这个要求。
答案 0 :(得分:11)
IIRC,在Java中,你可以使用超时来object.wait()。这不是你想要的吗?如果要更改另一个线程的超时,请更改一些'waitValue'变量并通知()。然后线程将“立即”运行,然后再次使用新的超时值。不需要明确的睡眠。
答案 1 :(得分:7)
This回答帮助我完成了这项工作。发布一些关于我如何实现它的代码。特别重要的是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();
}
}
}
答案 2 :(得分:5)
我不清楚你真正想做什么。你的目标是停止在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循环的线程与尝试更改间隔和停止任务的主线程之间的正确同步(请参阅here以获取有关java内存模型的更多信息的链接)。如您所见,在调用停止任务之后,继续使用PeriodicTask实例的线程。另请注意,PeriodicTask在收到中断的异常时会调用当前线程的中断。这确保在当前线程上设置中断标志,以便任何外部代码能够看到中断并做出适当的反应,例如,而不是打印完成,运行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的实例由池中的线程运行(但确保修复您的代码以便正确同步),并且,当使用中断时,您可以在要中断的线程的实例上调用它。如何获得对该线程的引用取决于您的系统。
答案 3 :(得分:1)
你在正在休眠的线程上调用interrupt()
,它将抛出你在catch块中处理的InterruptedException
。然后你有了新的间隔,你可以循环然后回去睡觉。如果你抓住并处理InterruptedException
没有进一步发生。
让我提供一些创建和中断线程的示例链接,从您的评论中您似乎缺少一些重要的想法。请仔细阅读这些内容,然后您应该了解执行所要求的标准方法。
http://docs.oracle.com/javase/tutorial/essential/concurrency/simple.html http://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html