Thread类中控制线程的方法

这篇文章中不会介绍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类中控制线程的方法)