目录
catch语句
catch语句简介
catch语句中的内容
线程的属性
Thread提供的属性和方法
线程的属性方法
前台线程
后台线程
是否存活
catch语句
catch语句简介
我们首先认识一下catch语句,catch语句是用来捕获异常,在catch代码块中可以在捕获异常后进行执行。当我们创建一个线程,并且让创建的线程等待主线程执行完毕后再进行执行,此时会调用start方法。
此时可以看到调用sleep()函数时发生了错误,这是为什么呢?通过查看源码:
sleep()函数抛出了一个Interrupted异常,也就是说在执行sleep函数时可能使程序在正常执行时被中断,此时我们可以使用try catch语句进行异常的捕获,保证程序的正常执行。我们给程序加上try catch语句:
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("thread 1 启动!!!");
}
}
}
public class Main {
public static void main(String[] args) {
Thread t1 = new MyThread();
t1.start();
for (int i = 0; i < 3; i++) {
System.out.println("main函数正常执行");
}
}
}
运行程序,可以看到程序正常执行,t1线程等待1s后开始执行,此时main线程已经执行完毕。
我们再回头看catch语句中的代码:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try语句表示捕获异常,包含需要抛出异常的方法,当异常被捕获后,执行catch代码块中的代码,catch中的throw new RuntimeException(e)表示抛出新的异常,也就是说我们此时对异常的处理是抛出异常,如果调用printStackTrace()方法,则是打印异常信息。
catch语句中的内容
在实际的开发中,catch中的代码是我们自己定义的,一般包括:
1.打印一些日志,把出现异常的详情都记录在日志中
2.触发重试类的操作
3.触发一些”回滚“类的操作
4.触发一些警报机制,用来提醒程序猿。
线程的属性
Thread提供的属性和方法
以下为构造方法
Thread(),创建线程对象。
Thread(Runnable target),使用Runnable对象创建线程对象。
Thread(String name),创建线程对象,并命名。
Thread(Runable,name),使用Runnable对象创建线程对象,并且命名。
补充: 是否起名字对于线程本身没有任何影响,起名在java程序运行过程中,可以通过工具看到不同线程的名称,方便调试,关于工具(jconsloe)的使用为在上一篇博客中已经讲过。如果没有起名字,每个线程都有默认的名字,如Thread-0,Thread-1,Thread-2······。
特别提醒:main线程的创建并没有通过重写run()方法来实现,这是应为main线程是jvm通过c++代码创建出来的,没有通过java中的Thread类创建,也就不会重写run()方法。
线程的属性方法
1.ID,使用getid()方法获取。(此处id和系统中的pcb的id不一样,java代码获取不到线程的pid,这里的id为jvm提供的一套id体系)。
2.名称,使用getName()方法获取。
3.状态,使用getStart()方法获取。(阻塞和就绪状态)
4.优先级,使用getPriority()方法获取。(java提供了优先级的接口,但即使修改了优先级,作用任然不大,这里的优先级仅仅为一个建议,具体以系统的调度为准。)
5.是否后台线程,使用idDaemon()方法获取。
6.是否存活,使用isAlive()方法获取。
7.是否被中断,使用isInterrupten()方法获取。
前台线程
这种线程如果不运行结束,java的进程不会结束,或者说程序就不会执行结束。因此有多个前台线程,直到最后一个前台线程结束时,程序才会结束。在java代码中,main线程就是前台线程。
后台线程
这种线程,即使在运行,java程序也不会结束。程序猿创建出来的线程,默认情况下都是后台进程。但是我们可以通过setDaemon方法把线程设置为后台线程。什么情况下我们需要把线程设置成后台线程呢?当我不期望这个线程影响进程结束。比如,负责gc(垃圾回收)的线程,gc是要周期持续性执行的,不可能主动结束,如果把它设置成前台进程,那么进程永远结束不了。
是否存活
指的是线程中的PCB是否还存在。
Thread对象的生命周期和PCB的生命周期是不一定完全一样的。当我们使用匿名内部类创建Thread实例:
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
});
System.out.println(t.isAlive());
}
}
此时,Thread对象已经诞生了,但内核中的PCB还没有诞生。运行代码:
我们此时使用isAlive方法测试线程是否存在,可以看到返回结果为false,说明此时内核中的PCB不存在,当我们执行t.start()方法时,才是真正在系统中创建链表(PCB才真正创建出来并加入到链表中)。修改代码,加上线程的创建。运行查看结果:
结果仍然是false,这是为何呢?我在判断线程存活之前加入了一个打印语句,因为此时的线程内部没有执行任何逻辑,当程序运行时,创建的线程t瞬间就被销毁了 ,即被gc回收了,我们换一种方式查看线程是否被创建:
public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
System.out.println("hello liangll");
System.out.println(t.isAlive());
}
}
此时,我让线程进入等待状态,那么它就不会被gc回收了,运行查看结果:
加入sleep()函数后,t线程在设置的短时间内不会被回收,此时可以看到main线程执行完打印函数之后返回的线程存活状态变为了true。此时,我们终于能通过isAlive()函数查看我们创建的线程了。