美文网首页Java学习笔记
Thread类中控制线程的方法

Thread类中控制线程的方法

作者: 爱说的夏老师 | 来源:发表于2017-07-03 17:16 被阅读32次

    这篇文章中不会介绍sleepwait, 会在其他的文章中介绍

    1. stop

    停止线程, 不推荐使用.

    JDK中已经标识为@Deprecated, 因为太暴力, 无论线程运行到什么状态, 都会强制停止, 可能会导致数据的不一致, 并且不会抛出异常.

    2. interrupt

    中断线程

    2.1 非阻塞状态的线程

    相当于一个中断的标志位, 调用之后, 只是把这个标志位改成true.
    可以通过interrupted()这个方法来判断中断状态.

    也就是说, 单纯的调用这个interrupt()方法, 是不会使得线程的运行中断的, 如果要中断线程的运行, 可以通过这样的代码来控制

    public void run(){ 
        while(true){ 
            if(Thread.currentThread().isInterrupted())
            { 
               System.out.println("Interruted!"); 
               break; 
            } 
            Thread.yield(); //这里可以是正常的线程执行操作
        } 
    }
    

    2.2 阻塞状态的线程

    对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false.
    理论上所有会throws InterruptedException的方法都是可以取消阻塞状态的.

    对于取消阻塞状态中的线程,可以这样写代码

    public void run(){
        while(true){
            if(Thread.currentThread().isInterrupted()){
                System.out.println("Interruted!");
                break;
            }
            try {
               Thread.sleep(2000);
            } catch (InterruptedException e) {
               System.out.println("Interruted When Sleep");
               //设置中断状态,抛出异常后会清除中断标记位
               Thread.currentThread().interrupt();
            }
            Thread.yield();//这里可以是正常的线程执行操作
        }
    }
    

    sleep的中断比较容易, 但是wait方法如果被中断, 不一定会马上抛出异常, 如果获取不到锁对象, 就会继续等待, 知道获取之后才会抛出InterruptedException.

    此外, interrupt()还可以在使用Lock对象的时候, 解决死锁的问题(可以参考Lock的lockInterruptibly()
    方法)

    3. suspend和resume

    线程挂起(suspend)和继续执行(resume)

    这两个方法都是Deprecated方法,不推荐使用。
    原因在于,suspend不释放锁,因此如果suspendsynchronized块中执行的,并且也没有其他线程来执行resume方法, 这个线程将一直占有这把锁,造成死锁发生。

    4. yield

    让出CPU资源

    这个让出只是一下, 执行完之后, 线程并不是变成等待状态, 而是从 "运行状态" 转换为 "就绪状态", 也就是说可能这个线程还是可能会马上抢占到CPU的资源
    官方说是可用于debug和test, 基本找不到使用的场景...

    5. join

    等待其他线程结束

    join的本质

    while (isAlive()) {
        wait(0);
    }
    

    详细的可以查看join的源码

    public static void main(String[] args) throws Exception {
        Thread r1 = new Thread(new MyThread());
        r1.start();
        r1.join();//等待r1线程执行完之后,才会继续执行下面的语句
        System.out.println("主线程结束");
    }
    

    上面的代码, 主线程在执行r1.join()的时候就会判断r1.isAlive(), 如果r1线程还活着, 就wait(0)

    既然是wait操作, 肯定会有锁对象. join方法是synchronized的, 所以调用这个方法的对象就是锁对象. 上面这个锁对象就是r1这个线程对象

    同样, 既然是wait, 肯定会有对应的notify来唤醒这个wait

    那么问题是哪里调用了notify呢?

    在join方法的javadoc中找到了解释:

    Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.

    This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

    意思是在每个线程结束之后, 都有调用this.notifyAll(), 那么这个操作可以理解为是由JVM自动完成的动作. 上面的代码则会在r1这个线程结束之后, JVM自动调用this.notifyAll(), 这里的this相当于r1, 然后把所有等待r1这个锁对象的线程给唤醒.

    所以javadoc中还给了我们一个建议,不要使用wait和notify/notifyAll在线程实例上。因为jvm会自己调用,有可能与你调用期望的结果不同。

    参考资料

    1. https://my.oschina.net/hosee/blog/599000

    相关文章

      网友评论

        本文标题:Thread类中控制线程的方法

        本文链接:https://www.haomeiwen.com/subject/cwlzcxtx.html