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

前端 未结 4 664
温柔的废话
温柔的废话 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 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.

提交回复
热议问题