start方法源码
- 对线程状态进行检查,0代表NEW状态
- 将线程加入线程组
- 调用native方法start0()
1 | public synchronized void start() { |
面试题1:一个线程两次调用start方法会出现什么情况?为什么?
会抛出IllegalThreadStateException。因为调用start方法时会对线程状态进行检查,不是NEW状态就会抛出IllegalThreadStateException。
面试题2:为什么选择调用start方法,而不直接调用run方法?
调用start方法才是真正的启动线程,会有线程的生命周期。而直接调用run方法,只是调用了一个普通方法。
如何正确停止线程:使用interrupt来通知,而不是强制
只是使用interrupt来通知线程,通过设置线程中断标志来实现,线程根据这个标志来自行处理。
interupted的三种情况
- 线程正常执行完成,调用interrpted方法
- 线程可能被阻塞
- 线程在每次迭代后都阻塞
栗子1:线程正常执行,需要线程自行根据中断标志来处理interrupt方法的通知
1 | public class InterruptDemo01 { |
执行结果:
1 | ...... |
栗子2:线程处在阻塞状态时,interrupt方法结束线程,抛出异常
将以上栗子1中的Thread方法改成以下:
1 | Thread thread1 = new Thread(() -> { |
执行方法:
thread处在sleep状态时,被中断抛出异常。
1 | Thread-0999997 is running |
栗子3:线程在迭代中处于阻塞状态,interrupt方法结束线程,抛出异常
将栗子1中的Thread方法修改为以下:
1 | Thread thread1 = new Thread(() -> { |
执行结果:
线程被中断后,抛出异常,catch捕获异常后,线程继续执行。
1 | Thread-00 is running |
栗子4:捕获异常的位置会影响执行结果
在栗子3的基础上,将捕获异常的代码块范围增大。
1 | Thread thread1 = new Thread(() -> { |
执行结果:
线程接收到中断通知后,会抛出异常。处理异常后,不再执行循环了,而是执行完线程接下来的任务就结束了。异常的捕获范围影响了线程的执行。
1 | Thread-00 is running |
栗子5:在迭代中try..catcha异常会导致interrupt方法失效
在栗子3中,我们在迭代中使用了try..catch..捕获了异常,线程继续往下执行。
那么,是不是加上isInterrupted方法的判断,捕获异常后,线程就不往下执行了呢?
1 | Thread thread1 = new Thread(() -> { |
执行结果:
线程在捕获了异常后,还是继续往下执行了。
1 | ...... |
interrupt()与interrupted()区别
interrupted方法,不管是谁调用,都只对当前线程有效
1 | public class InterruptDemo02 { |
执行结果:
t1.interrupt()设置了中断标志=true,isInterrupted()返回中断标志true,不会清除中断标志
1 | Thread-0:true |
如果改成interrupted()方法呢?
1 | Thread t1 = new Thread(() -> { |
执行结果:
t1.interrupt()设置了中断标志=true,Thread.interrupted()返回中断标志=true,并且清除了中断标志=false
下面的输出语句输出中断标志=false
1 | Thread-0:false |
interrupt方法源码
intertupt方法只是设置线程中断标志。
1 | public void interrupt() { |
相关方法:
1 | // 不清楚中断标志 |
中断线程的两种姿势
- 不在线程方法内部try..catch,把异常抛出去,由调用方处理中断异常
- 在线程方法内部try..catch,并在catch后,再次中断线程,恢复中断,这样方法调用方能处理中断
响应线程中断的N种方法
- Object :wait
- Thread:sleep,join
- BlockQueue:take/put
- Lock:lockInterruptibly
- CountDownLatch:await
- CyclicBarrier:await
- Exchanger:exchange
- java.nio.channels.InterruptibleChannel相关方法
- java.nio.channels.Selector的相关方法
错误的停止线程的方法
- stop/suspend/resume
- 用volatile设置的boolean标记位,在阻塞的时候,线程不会响应标记位的更新
栗子1:生产者与消费者模式证明在阻塞的时候,使用volatile设置的标记位来停止线程是错误的
1 | public class ProducerAndConsumer { |