页面加载中...

Java如何停止线程

| Java | 0 条评论 | 471浏览

Preface

启动一个线程或任务都是很简单,线程一般在任务结束后便会自行停止。但是有时我们希望能够在线程自行停止前能够停止它们,比如一些取消操作,或者是应用程序需要快速关闭。博主日前就遇到了这样的问题。

但是在《JAVA并发编程实践》一书中指出:

Java没有提供任何机制,来安全地强迫停止手头地工作。

 

一般来讲,对于Runnable来说,需要做的任务都是在run方法里面进行的,停止一个线程也可以认为是从run方法跳出来吧。

线程停止

使用标志位

这种方式的基本思想为:使用一个标志位,通过这个标志位来决定run方法是继续执行还是直接结束。如下:


  static class CancelledTaggedRunnnable implements Runnable{
        private volatile boolean cancelled = false;
        @Override
        public void run() {
            while(!cancelled){
             //没有停止需要做什么操作
            }
            //线程停止后需要干什么
            System.out.println("任务结束了");
        }
        public void cancell(){
            cancelled = true;
        }
    }

定义一个标志位:cancelled,cancelled为true是即run方法结束,反之继续while里面的任务。通过cancell方法来停止任务。

写一个测试类:

@Test
public void testCancelledTaggedRunnnable() throws InterruptedException {
    CancelledTaggedRunnnable taggedRunnnable = new CancelledTaggedRunnnable();
    Thread thread = new Thread(taggedRunnnable);
    thread.start();
    System.err.println(thread.isAlive());
    Thread.sleep(1000);
    taggedRunnnable.cancell();
    Thread.sleep(1000);
    System.err.println(thread.isAlive());
    Thread.sleep(1000);
    System.err.println(thread.isAlive());
}

上述代码的操作为:启动该线程,1s秒调用cancell方法,从而来停止该线程。

中断策略

但是使用标志位这种方法有个很大的局限性,那就是通过循环来使每次的操作都需要检查一下标志位。

Java还提供了中断协作机制,能够使一个线程要求另外一个线程停止当前工作。其大致的思想为:调用线程Thread的interrupt方法,在线程内部通过捕获InterruptedException异常来决定线程是否继续还是退出。如下:

    static class InterruptRunnable implements Runnable{
        private BlockingQueue queue = new ArrayBlockingQueue(10);
        @Override
        public void run() {
            int i= 0;
            for (;;) {
                try {
                    //线程的操作
                    i++;
                    queue.put(i);
                } catch (InterruptedException e) {
                    //捕获到了异常 该怎么做
                    System.out.println(queue);
                    e.printStackTrace();
                    return;
                }
            }
        }
    }

上述代码通过BlockingQueue的put方法来抛出InterruptedException异常。当内部捕获到该异常时,从而决定是否继续还是直接退出了。

写个测试方法:

    @Test    
public void testInterruptRunnable() throws InterruptedException {
        InterruptRunnable runnable = new InterruptRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
        System.err.println(thread.isAlive());
        Thread.sleep(1000);
        thread.interrupt();
        Thread.sleep(1000);
        System.err.println(thread.isAlive());
        Thread.sleep(1000);
        System.err.println(thread.isAlive());
    }

该测试方法大致同使用标志位的测试方法,同样启动该线程后,1秒后调用线程的interrupt方法,从而触发Runnable的内部queue.put(i)操作抛出InterruptedException异常。  

强行停止(不推荐)

对于多线程,使用中断策略 较为优雅,也是官方的推荐。线程停止前你应该做一些操作,从而保证该线程运行后的数据不会被丢失,这一点在多线程中极为重要。

但是对于中断策略还是有一个很大的缺陷,那就是,必须通过中断的阻塞函数,如:我使用的BlockingQueue的put方法,也可以是Thread.sleep()方法,才能抛出InterruptedException。如果抛不出这样的异常呢?

对于单线程,还有一个简单粗暴的方式,那就是Java已经不再使用的Thread  stop方法。

使用方法即直接调用:thread.stop()即可。

如果你对该线程的操作不再关心了,对结果也不再在意了,使用该方法也是可以的。

但多线程情况下,或者线程带锁的情况下 那就要慎用了。该方法不安全

使用future任务

 Java还提供一个可带返回值的线程操作,FutureTask。在这个类中可以调用其cancel方法来取消这个任务。

你可以设置true或者false来决定是否让线程出现中断的操作。从而可以使这个中断后能够捕获InterruptedException来决定是否让操作结束。

一般来说,这个类都是跟带返回值的Callable接口一起使用,Runnable接口也是可以使用的,定义线程的操作:

static class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (; ; ) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("线程被中断");
                return;
            }
            System.out.println("程序运行中....");
        }
    }
}

上述代码较简单,不需要多说了。使用FutureTask进行测试,先不让产生中断:

@Test
public void test01() throws Exception {
    FutureTask task = new FutureTask<>(new MyRunnable(), 0);
    ExecutorService pool = Executors.newFixedThreadPool(1);
    pool.submit(task);
    Thread.sleep(1000);
    System.out.println("任务是否完成?" + task.isDone());
    System.out.println("任务是否被取消?" + task.isCancelled());
    Thread.sleep(1000);        //在这里取消操作        
    System.out.println(task.cancel(true));        
    Thread.sleep(1000);
    System.out.println("任务是否完成?" + task.isDone());
    System.out.println("任务是否被取消?" + task.isCancelled());
    Thread.sleep(5000);
}

通过线程池的方式来提交任务,让其运行,2秒后取消任务,查看控制台:

但是FutureTask的cancelle方法并不能真正的停止当前线程,其主要思想还是如同中断策略。也就是说,如果我们不设置task.cancell(true),那么任务还不会被中断,如,我们改为task.cancell(false),结果如下:

虽然此时调用了task.cancell(false)方法,但是通过isDone,isCancelled方法并不能说名任务就结束了。如上,任务还在继续。

综上,Java并没有停止线程的方法,如果需要手动的停止,还是建议较优雅的中断策略,或者FutureTask。 

发表评论

最新评论

    来第一个评论吧!