如何停止线程

2020-01-15  本文已影响0人  ywy_袁滚滚

前言

线程的使用想必大家都很熟悉,那么关于如何停止一个线程,或者说如何正确的停止一个线程,尤其是在涉及到锁、多个线程需要交互的情况,应该如何去停止线程来保证程序的行为符合自己的预期呢?

停止线程,我觉得首先需要明确自己想要停止线程的'停止'到底需要线程干什么。是不再需要线程执行任务了,让线程直接TERMINATED,还是只是需要线程暂时挂起一段时间,比如WAITINGTIMED_WAITING,等符合条件之后再被唤醒继续执行。如果不太了解TERMINATEDWAITINGTIMED_WAITING指的是什么,可以先看看Java线程的状态这篇文章。

线程停止的方法

Java中的Thread提供了停止线程和挂起线程的方法,但是它们都被废弃了

Deprecated: This method was originally designed to force a thread to stop and throw a ThreadDeath as an exception. It was inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior. Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method should be used to interrupt the wait. For more information, see Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

Deprecated: This method was designed to suspend the Thread but it was inherently deadlock-prone. If the target thread holds a lock on the monitor protecting a critical system resource when it is suspended, no thread can access this resource until the target thread is resumed. If the thread that would resume the target thread attempts to lock this monitor prior to calling resume, deadlock results. Such deadlocks typically manifest themselves as "frozen" processes. For more information, see Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?

线程停止的方法

Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method should be used to interrupt the wait.

简单翻译下就是可以通过修改一些变量以指示目标线程应该停止运行,目标线程应该定期检查这个变量,如果该变量表示它将停止运行,则以有序的方式从其run方法返回。如果目标线程等待很长时间(例如,在一个条件变量上),应该使用interrupt方法来中断等待。

Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

If this thread is blocked in an invocation of the Object#wait(), Object#wait(long), or Object#wait(long, int) methods of the Object class, or of the join(), join(long), join(long,int), sleep(long), or sleep(long,int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a .

If this thread is blocked in a java.nio.channels.Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup() method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Interrupting a thread that is not alive need not have any effect.

Java线程提供了中断方法interrupt(),当然它不是真正的打断线程的运行,它是native方法,原理就是利用一个标记记录线程的中断状态,也就是记录线程有没有被其它线程执行了中断操作。调用它仅仅只是为线程打了一个中断的标记。而线程可以通过静态方法Thread.interrupted() 或成员方法isInterrupted()来感知其它线程对自己的中断操作从而作出相应的响应

注释中也说明了,当线程处于WAITING,TIMED_WAITING或在进行I/O操作阻塞时,调用interrupt()会接收到一个InterruptedException,并且中断状态也会被清除

class ThreadTest {

    @Test
    fun test() {
        val thread = MyThread("thread 1")

        thread.start()

        thread.interrupt()

        println("thread.isInterrupted():${thread.isInterrupted()}")

        Thread.sleep(6000)
        println("thread state:${thread.getState()}")
        println("主线程等待6s结束")
    }


    class MyThread constructor(name: String) :
        Thread(name) {
        var count = 0
        override fun run() {
            super.run()
            while (true) {
                //do sth
                if (isInterrupted()) {
                    count++
                    if (count < 5) {
                        println("中断状态:${isInterrupted()}")
                    }else{
                        println("结束线程")
                        break
                    }
                }
            }
        }
    }
}
中断并结束.png
class ThreadTest {

    @Test
    fun test() {
        val thread = MyThread("thread 1")

        thread.start()

        thread.interrupt()

        println("thread.isInterrupted():${thread.isInterrupted()}")

        Thread.sleep(6000)
        println("thread state:${thread.getState()}")
        println("主线程等待6s结束")
    }


    class MyThread constructor(name: String) :
        Thread(name) {
        var count = 0
        override fun run() {
            super.run()
            while (true) {
                //do sth
                try {
                    sleep(500)
                    if (isInterrupted()) {
                        count++
                        if (count < 5) {
                            println("中断状态:${isInterrupted()}")
                        }else{
                            println("结束线程")
                            break
                        }
                    }
                }catch (e:InterruptedException){
                    println("接收到 InterruptedException,中断状态:${isInterrupted()}")
                }

            }
        }
    }
}

isInterrupted()和interrupted()的区别

前面提到成员方法isInterrupted()和静态方法interrupted()都能感知到中断操作。那么它们之间有什么区别?

class ThreadTest {

    @Test
    fun test() {
        val thread = MyThread("thread 1")

        thread.start()


        thread.interrupt()

        println("thread.isInterrupted():${thread.isInterrupted()}")

        Thread.sleep(6000)
        println("thread state:${thread.getState()}")
        println("主线程等待6s结束")
    }


    class MyThread constructor(name: String) :
        Thread(name) {
        var count = 0
        override fun run() {
            super.run()
            while (true) {
                //do sth
                count++
                if (count < 5) {
                    println("中断状态 isInterrupted():${isInterrupted()}")
                    println("中断状态 interrupted():${Thread.interrupted()}")
                } else {
                    println("结束线程")
                    break
                }
            }
        }
    }
}

乍一看好像也没有区别,输出的结果都是一致的,实际上这也正是它们的区别导致的。通过前面的例子,我们可以发现,执行了中断操作后,多次调用isInterrupted()的返回结果一直返回true,当然抛InterruptedException之后由于中断状态被清除,isInterrupted()的返回结果为false。而执行了中断操作后,第一次调用interrupted()的结果为true,而且调用interrupted()也会清除中断状态,所以之后的中断状态一直为false,只有再次执行中断操作,才会返回true

在了解了两者的区别之后,针对不同的需求才能更好的选择使用哪个方法来监听中断状态

打中断标记

官方提供的中断方法是native的,我们知道jni调用多多少少还是有一点性能上的消耗的。所以我们可以自己给线程定义一个中断标记

class ThreadTest {

    @Test
    fun test() {
        val thread = MyThread("thread 1")

        thread.start()

        Thread.sleep(2)
        thread.stop = true
        Thread.sleep(6000)
        println("thread state:${thread.getState()}")
        println("主线程等待6s结束")
    }


    class MyThread constructor(name: String) :
        Thread(name) {
        //volatile保证可见性
        @Volatile var  stop = false
        override fun run() {
            super.run()
            while (true) {
                if (stop){
                    break
                }
                println("do sth")
            }

            println("线程结束")
        }
    }
}
中断标记.png
上一篇 下一篇

猜你喜欢

热点阅读