1、判断线程存活
1. 当线程run()或者call()方法执行结束,线程进入终止状态
2. 当线程内发生异常,并且异常没有被捕获,线程进入终止状态
3. 线程调用stop()方法后,线程进入终止状态(不推荐使用)
当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受主线程的影响。
为了测试某个线程是否已经死亡,可以调用线程对象的isAlive()方法,当线程处于就绪、运行、阻塞3种状态时,该方法将返回true;当线程处于新建、死亡2种状态时,该方法将返回false。
/**
* isAlive()方法练习
*/
public class IsAliveDemo1 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
});
System.out.println("线程的状态:"+t.getState());//NEW
System.out.println("线程启动前:"+t.isAlive());//false
System.out.println("----------------------");
t.start();
System.out.println("线程的状态:"+t.getState());//RUNNABLE
System.out.println("线程启动后:"+t.isAlive());//true
System.out.println("----------------------");
TimeUnit.SECONDS.sleep(5);
System.out.println("线程的状态:"+t.getState());//TERMINATED
System.out.println("线程结束后:"+t.isAlive());//false
}
}
/**
*isAlive()方法练习2
*/
public class IsAliveDemo2 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for (int i = 0; i < 40; i++) {
System.out.println(i);
if(i==15){
synchronized (IsAliveDemo2.class){
try {
IsAliveDemo2.class.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
});
//启动前获取状态
System.out.println("线程启动前:"+t.isAlive());
t.start();
System.out.println("线程启动后:"+t.isAlive());
//主线程休眠2秒,给予t线程充足的执行时间
TimeUnit.SECONDS.sleep(2);
System.out.println("线程抛出异常后:"+t.isAlive());
}
}
注意:不要对处于死亡状态的线程调用start()方法,程序只能对新建状态的线程调用start()方法,对新建状态的线程两次调用start()方法也是错误的。这都会引发IllegalThreadState Exception异常。
/**
* 练习3
*/
public class IsAliveDemo3 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
});
t.start();
//t.start(); 抛出IllegalThreadStateException异常
TimeUnit.SECONDS.sleep(2);
System.out.println(t.getState());//TERMINATED
//t.start(); 抛出IllegalThreadStateException异常
}
}
2、线程控制
(1)Join()
join()方法相当于插入,例如在T2线程中调用了线程T1.join()方法,则T2线程会一直等待T1线程执行结束后再继续执行。
就是在A线程中调用线程B的join()方法,线程A会一直等待直到线程B执行结束再执行,也可以理解为插队。
注意:当T2线程执行过程中被T1线程Join,线程T1执行时,T2处于WAITING
/**
* Join()方法
*/
public class JoinDemo1 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName()+"--------->"+i);
}
},"t1");
Thread t2 = new Thread(()->{
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName()+"#########"+i);
if (i == 10){
try {
//t1线程调用t2线程的join()方法,插队执行
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
},"t2");
t1.start();
System.out.println(t1.getState());
t2.start();
//main t1 t2 三个线程交替执行
for (int i = 0; i < 60; i++) {
System.out.println(Thread.currentThread().getName()+"***********"+i);
}
}
}
(2)Join(long millis)
如果T2线程执行过程中调用为了T1线程的join(long millis)方法,T2线程最多等待T1线程millis毫秒,到达时间后,如果T1线程没有结束,则和T2线程交替执行。
注意:T2线程被T1线程join(millis)后,T2线程在等待T1线程执行的过程中处于TIMED_WAITING状态
/**
* Join()方法
*/
public class JoinDemo2 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"-------->"+i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"t1");
Thread t2 = new Thread(()->{
for (int i = 0; i < 200; i++) {
System.out.println(Thread.currentThread().getName()+"##########"+i);
if (i == 20){
//启动t1线程
t1.start();
try {
//在线程t2中插入
t1.join(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
},"t2");
//启动进程
t2.start();
System.out.println("--------------------");
//主线程休眠 让t2线程执行
TimeUnit.SECONDS.sleep(2);
//t2线程的状态
System.out.println(t2.getState());//TIMED_WAITING
}
}
(3)守护线程
在后台运行的,并为其他的线程提供服务的线程被称为“后台线程”,又称为“守护线程”,JVM的垃圾回收线程就是典型的后台线程。
特征:如果所有的前台线程都死亡,后台线程会自动死亡。
守护线程必须在启动前将其守护状态设置为true,启动之后不能再将用户线程设置为守护线程,否则JVM会抛出一个InterruptedException异常。具体来说,如果线程为守护线程,就必须在线程实例的start()方法调用之前调用线程实例的setDaemon(true),设置其daemon实例属性值为true。
守护线程存在被JVM强行终止的风险,所以在守护线程中尽量不去访问系统资源,如数据库连接。守护线程被强行终止时,可能会引发系统资源操作不负责任的中断,从而导致资源不可逆的损坏。
守护线程创建的线程也是守护线程。在守护线程中创建的线程,新的线程都是守护线程。在创建之后,如果通过调用setDaemon(false)将新的线程显式地设置为用户线程,新的线程可以调整成用户线程。
/**
* setDaemon()方法
*/
public class DaemonDemo {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"===="+i);
}
},"t1");
//设置线程为守护线程 必须在启动前设置
t1.setDaemon(true);
t1.start();
//主线程循环次数大幅少于守护线程,当前台线程执行结束时 守护线程不管是否执行完毕都会结束
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+">>>>"+i);
}
}
}