这篇文章中不会介绍
sleep
和wait
, 会在其他的文章中介绍
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
不释放锁,因此如果suspend
在synchronized
块中执行的,并且也没有其他线程来执行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会自己调用,有可能与你调用期望的结果不同。