一. 启动线程的方式
1.run和start启动方式区别?
/**
* create on 2020-12-23 13:17
* run方式启动是由主线程来启动,start方式会新起一个线程来启动
**/
public class CreateThreadAndStart {
public static void main(String[] args) {
Runnable runnable = ()->{
System.out.println(Thread.currentThread().getName());
};
runnable.run();
Thread t = new Thread(runnable);
t.start();
}
}
2.start()方法的含义
- 启动新线程
- 准备工作
- 不能重复start()
3.start()源码解析
- 启动新线程之前会先检查当前线程状态,只有NEW状态下的线程才会继续,否则会抛IllegalThreadStateException
- 加入线程组中
- 调用start0()(native方法,c++实现)方法
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
注意点:start方法是被synchronized修饰的方法,可以保证线程安全。由JVM创建的main方法线程和system组线程,并不会通过start方法启动。
4.run()方法源码解析
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
5.常见面试题
- 一个线程两次调用start()方法会出现什么情况?为什么?
- 既然start()方法会调用run()方法,为什么我们不直接去调用run()方法?
二.如何正确停止线程
原理介绍:使用 interrupt 来通知而不是强制停止
注意点:
在线程迭代过程中停车线程会以抛出interruptException异常作为中断信号
普通情况下需要配合Thread.currentThread.isInterrupt才能中断线程
如果while循环里面使用try/catch中断会失效(标记位会被清除)
如果不能抛出中断,就得传递中断,在字方法中try/catch中调用Thread.currentThread().interrupt()
响应中断的方法总结:
Object.wait()/wait(long)/wait(long,int)
Thread.sleep(long)/sleep(long,int)
Thread.join()/join(long)/join(long,int)
java.util.concurrent.BlockingQueue.take()/put(E)
java.util.concurrent.locks.Lock.lockInterruptibly()
java.util.concurrent.CountDownLatch.await()
java.util.concurrent.CyclicBarrier.await()
java.util.concurrent.Exchanger.exchange(V)
java.nio.channels.InterruptibleChannel相关方法
java.nio.channels.Selector的相关方法
三.Java异常体系
StackOverflowError
VirtualMachineError ...
... OutOfMemoryError
Error
AWTError
Throwable AirthmeticException
...
RuntimeException NullPointerException
IndexOutOfBoundsException
Exception ...
EOFException
IOException ...
FileNotFoundException
四.常见面试题
如何停止线程?
A.用volatile的boolean作为标记来停止
B.用stop()方法来让线程停止
C.用interrupt来请求线程停止
解答:C
1.原理:用interrupt来请求线程停止,不是强制
2.想停止线程,要求请求发、被停止方、字方法被调用方互相配合才可以:
a)作为请求方:每次循环中或者适时检查中断信号,并且在可能抛出InterruptedException的地方处理中断信号
b)请求方:发出中断信号
c)字方法调用方(被线程调用的方法的作者)要注意:优先在方法层面抛出InterruptedException,或者检查到中断信号时,再次设置中断状态(传递中断信号)
3.最后再说错误的方法:stop/suspend已废弃,volatile的boolean无法处理长时间堵塞的情况
无法响应中断时如何停止线程?
A.用interrupt方法来请求停止
B.不可中断的阻塞无法处理
C.根据不同的类调用不同的方法
解答:C
如果线程阻塞是由于调用了wait(),sleep()或join()方法,你可以中断线程,通过抛出interruptedException异常来唤醒该线程
但是对于不能响应InterruptedException的阻塞,很遗憾并没有一个通用的解决方法
但是我们可以利用特定的其他的可以响应中断的方法,比如ReentrantLock.lockInterruptibly(),比如关闭嵌套字使线程立即返回等方法来达到目的。
答案有很多种,因为很多原因会造成线程阻塞,所以针对不同情况唤起的方法也不同
总结就是如果不支持响应中断,就要用特定的方法来唤醒,没有万能药。