首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >唤醒睡眠线程--中断()与将睡眠“分割”成多个睡眠

唤醒睡眠线程--中断()与将睡眠“分割”成多个睡眠
EN

Stack Overflow用户
提问于 2012-01-03 05:57:20
回答 3查看 37.4K关注 0票数 5

这一要求出现在我的Android应用程序中,但它一般适用于Java。我的应用程序“做些什么”每几秒钟。我已经按照以下方式实现了这一点(只是相关的片段-不是完整的代码):

Snippet1:

代码语言:javascript
复制
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()不能立即生效。只有在以前的睡眠()完成之后,它才会生效。

由于我的用例允许最终用户以固定的步骤(1秒-从1到60秒)设置时间间隔,所以我将实现修改为在循环中休眠;并且每秒钟检查一次新的间隔值如下:

Snippet2:

代码语言:javascript
复制
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呢?

代码语言:javascript
复制
public void setInterval(int newInterval){
        this.interval = newInterval;
        //What thread do I call interrupt() on?
    }

其次,如果我成功地中断了休眠的Thread,我相信将执行InterruptedExceptioncatch块。但是,此时我需要再次调用startTask()。我对这一递归的终止感到困惑。关于中断()的使用,我已经问过几个问题,但是找不出对我有帮助的任何东西。

有什么指示吗?

编辑-关于确切要求的更多细节:

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

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

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

确切的要求是,新的更新间隔应该立即生效(或者至少在设置后的1秒内生效,因为这是用户可能会感知到的)。

我的Snippet2和Snippet3是实现这一要求的尝试。

EN

回答 3

Stack Overflow用户

发布于 2012-01-04 13:42:57

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

代码语言:javascript
复制
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();
        }
    }
}
票数 7
EN

Stack Overflow用户

发布于 2012-01-04 06:15:26

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

代码语言:javascript
复制
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 );
        }
    }
}

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

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

代码语言:javascript
复制
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的实例是由池中的一个线程运行的(但是一定要修复您的代码才能正确地同步),在使用中断时,您可以在要中断的线程的实例上调用它。如何获得对该线程的引用取决于系统。

票数 5
EN

Stack Overflow用户

发布于 2012-01-03 06:04:29

在正在休眠的线程上调用interrupt(),它将抛出在catch块中处理的InterruptedException。然后,你有了新的间隔,你可以绕圈,然后回去睡觉。如果您捕获并处理InterruptedException,则不会再发生任何事情。

让我提供几个创建和中断线程的例子链接,因为从您的评论中您似乎缺少了一些重要的想法。请仔细回顾这些,然后你应该理解标准的方式去做你想要的。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8708473

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档