唤醒睡眠线程 - 中断()与将睡眠"分裂"为多个睡眠

cur*_*zen 5 java multithreading android sleep

这个要求出现在我的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这种方法的问题是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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

其次,如果我成功中断了睡眠Thread,我相信会执行该catchInterruptedException.但是,此时我需要startTask()再次打电话.关于这个递归的终止,我很困惑.关于trunk()的使用,我已经经历了几个关于SO的问题,但是找不到任何可以帮助我的东西.

有什么指针吗?


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

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

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

  • 线程进入睡眠状态60秒.
  • 现在,假设用户将更新间隔更改为5秒.线程仍在休眠.
  • PeriodicTask只能看到经过60秒的已经过期了新的更新间隔.

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

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

Mar*_*mes 11

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


cur*_*zen 7

这个答案帮助我完成了这项工作.发布一些关于我如何实现它的代码.特别重要的是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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


phi*_*lwb 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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这与您现有的代码非常相似.请注意volatile字段,以确保运行PeriodicTask循环的线程与尝试更改间隔和停止任务的主线程之间的正确同步(请参阅此处获取有关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 );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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