回顾上文:我们知道
- 我们可以设置线程为后台线程,通过
setDaemon()这个方法,后台线程有一个特征,就是程序结束时会直接杀死所有的后台线程,而程序中如果含义非后台线程,程序就无法关闭。- 单独设置线程的属性会造成大量重复的代码,可以使用线程工厂去做一些线程通用的配置事情。一个类通过实现
ThreadFactory接口就表示该类是一个线程工厂类,通过newThread返回线程。
本文内容:
- 通过继承Thread类,实现线程与任务的相结合。
- 学习使用线程对象的
join()的用法。- 打断一个线程,用
interrupt去设置一个标志,并非直接关闭线程。- 如何捕获在线程中抛出的异常。
继承Thread类
这种并不是定义一个任务,而是将线程和任务做结合,适合一些单一线程工作的场景。一个类直接继承Thread类,重写run()方法即可。
public class Demo extends Thread{
@Override
public void run() {
System.out.println("你好");
}
public Demo(){
start();
}
}
该构造方法中直接调用了start()方法,表示new Demo()之后即会开启线程执行run()方法。
线程中加入线程
这种加入线程比较特殊,它可以使执行的方式变成顺序执行。比如A线程中调用了B线程的join方法,则A线程会等待B线程执行完毕后,再继续执行。
想使用该join()方法,要理解一点,该方法是线程对象调用的,所以我们需要在该线程中传入其他线程的对象。
我们看一个示例,在Main函数控制的线程中,将Demo线程加入(join)进来,那么主线程的 System.out.println("nihao");就需要等待Demo执行完毕之后再执行,即使Demo中调用了sleep,主线程依然要等待。
public static void main(String[] args) {
try {
new Demo(new Thrad1()).join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("nihao");
}
Interrupt的一些理解
join方法可以让一个线程等待另外一个线程,如果想打断这个join呢,或者换句话说,如何让一个线程停止呢。
历史
suspend()和stop()方法作为原先的方法,已经在文档中被标注为过时。suspend容易导致死锁;stop不安全。所以知道存在即可。
interrupt
作为一种新的结束线程(Thread)的解决方案,但是注意,thread对象调用interrupt方法,并不会将该线程结束掉,就像这样子。
// main
Thread thread = new Thread(new Runnable..);
thread.start();
thread.interrupt();
在main函数中调用thread的start方法正常开启的一个线程,会去执行runnable中的run方法。接着我们再去执行thread.interrupt();会发现thread线程并没有被打断,会依然正常将run方法执行完毕。
原因:
interrupt并非直接将线程杀死(结束),而是给这个线程赋予一个中断状态。所以我们可以通过在线程中通过isInterrupted查看当前线程是否被终断,如果中断则退出业务逻辑。
捕获线程的异常
在线程任务执行run时,如果用throw抛出的异常,你无法在run方法外捕获这种异常,它只会输出在控制台中。
这个Thread1中有个除0的错误,用throw抛出了。
static class Thread1 extends Thread{
@Override
public void run() {
try {
System.out.println(1/0);
}catch (Exception e){
throw e;
}
}
}
主线程通过start执行该线程,用try...catch尝试捕获异常
Thread1 thread1 = new Thread1();
try {
thread1.start();
}catch (Exception e){
System.out.println("捕获到异常" + e);
}
执行后发现并未捕获到异常。
Thread.UncaughtExceptionHandler
该接口是专门用来捕获线程中抛出异常的,使用起来很简单:
- 实现该接口
- 重写
uncaughtException方法
示例:
public class UncaughtExceptionHandleUtils implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程: " + t + " 异常原因: " + e);
}
}
配置该异常处理类
我们已经定义好异常捕获器了,现在需要将它配置在相应的线程上。归根到底还是在线程对象上设置该异常捕获器,有三种设置方式:
- 线程本身设置
- 使用线程工厂设置
- 默认捕获器
线程本身设置
很简单,该线程对象调用setUncaughtExceptionHandler即可,将异常捕获器作为参数传入即可。
Thread1 thread1 = new Thread1();
thread1.setUncaughtExceptionHandler(new UncaughtExceptionHandleUtils());
thread1.start();
使用线程工厂
道理和上个方法一样,只是线程工厂可以重复使用,做统一的操作,如果你想创建的每个线程都有这个异常捕获器。线程工厂实现ThreadFactory接口即可。
默认捕获器
下例,在main函数中通过Thread.setDefaultUncaughtExceptionHandler设置了默认的异常捕获器。
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandleUtils());
Thread1 thread1 = new Thread1();
thread1.start();
}
默认捕获器作为异常捕获的最后一步,如果没有指定uncaughtException,就会用DefaultUncaughtExceptionHandler。