Waking up a sleeping thread - interrupt() versus “splitting” the sleep into multiple sleeps

前端 未结 4 659
温柔的废话
温柔的废话 2021-02-10 10:40

This requirement came up in my Android app, but it applies to Java in general. My app \"does something\" every few seconds. I have implemented this as follows (just relevant sni

相关标签:
4条回答
  • 2021-02-10 10:46

    This answer helped me do the job. Posting some code on how I achieved it. Of particular importance are startTask() and 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();
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-10 11:01

    You call interrupt() on the thread that's sleeping and it will throw an InterruptedException which you handle in your catch block. Then you have your new interval and you can loop around and go back to sleep. If you catch and handle the InterruptedException nothing further happens.

    Let me provide a couple of links of examples of creating and interrupting threads, as from your comments you seem to be missing some important ideas. Please review these carefully and then you should understand the standard way to do what you are asking for.

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

    0 讨论(0)
  • 2021-02-10 11:04

    I'm not clear on what you really want to do. Is you goal to stop the thread that is running the loop in PeriodicTask or do you just want to break the loop and allow the thread to continue? If you just want to break the loop but allow the thread to continue, consider the following example:

    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 );
            }
        }
    }
    

    This is very similar to your existing code. Note the volatile fields to ensure correct synchronization between the thread running the PeriodicTask loop and the main thread that attempts to change the interval and to stop the task (see here for links to more information on the memory model for java). As you can see, the thread that works with the PeriodicTask instance continues after the call to stop the task. Also, note that PeriodicTask will call interrupt on the current thread when it receives the interrupted exception. This ensures that the interrupt flag is set on the current thread so that any outer code is able to see the interrupt and react appropriately, e.g. instead of printing done, the thread running PeriodicTask may have checked the interrupt status of itself and done something interesting.

    If your goal is to stop the thread itself, then you may want to have PeriodicTask extend Thread, which is not recommended unless you have a good reason to do so, or, have PeriodicTask implement Runnable. Consider the next example:

    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 );
            }
        }
    }
    

    Here, PeriodicTask sits in a busy loop until the thread running it is interrupted. The interrupt is used to signal the PeriodicTask to exit the loop, which then allows the thread to complete by falling through the end of the run method.

    Regarding your two explicit questions: no, I don't see any real problem with using PeriodicTask the way you are if you aren't intending to control the executing thread, e.g. maybe the instance of PeriodicTask is run by a thread in a pool (but do be sure to fix your code to be correctly synchronized), and, when using interrupt you call it on the instance of the thread that you want to interrupt. How you get a reference to that thread is dependent on your system.

    0 讨论(0)
  • 2021-02-10 11:08

    IIRC, in Java you can object.wait() with a timeout. Is this not what you want? If you want to change the timeout from another thread, change some 'waitValue' variable and notify(). The thread will then 'immediately' run and then wait again with the new timeout value. No explicit sleep required.

    0 讨论(0)
提交回复
热议问题