1 线程的生命周期
在线程的生命周期中,要经过新建new
、就绪runnable
、运行running
、阻塞blocked
和死亡dead
5种状态。
当线程启动后,它不可能一直“霸占”CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行和阻塞之间切换。
线程的生命周期五个阶段
1、新建状态,当程序使用new
关键字创建了一个线程之后,该线程处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值。
2、就绪状态,当线程对象调用了start
方法后,该线程处于就绪状态。java虚拟机会为其创建方法调用栈和程序计数器,等待调试运行。
3、运行状态,如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。
4、阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态。
5、死亡状态,在线程的生命周期当中,线程执行完成之后的最终状态。
2 线程的休眠
线程休眠的方法是 Thread.sleep(long millis)
线程休眠的目的是使线程让出CPU的最简单做法之一,线程休眠的时候,会将CPU资源交给其他线程,以便能轮换执行,当休眠一定时间后,线程会苏醒,进入就绪状态等待执行。
简单说,哪个线程调用sleep()
方法,就休眠哪个线程。
3 线程的让步
yield()
的作用是让步。它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行权。
但是,并不能保证在当前线程调用yield()
之后,其它具有相同优先级的线程就一定能获得执行权;也有可能当前线程又进入到“运行状态”继续运行。
简单的说,就是哪个线程执行yield
方法,就是礼让。
public class UserThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " :A");
Thread.yield();
System.out.println(Thread.currentThread().getName() + " :B");
}
}
4 线程的合并
线程的合并作用,就是将几个并行线程合并为一个单线程执行。
当一个线程必须等待另一个线程执行完毕才能执行时,可以使用join()
方法。
public class UserThread extends Thread {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println(Thread.currentThread().getName() + "执行" + i + "次");
}
}
}
public class Test {
public static void main(String[] args) {
UserThread userThread = new UserThread();
userThread.start();
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName() + "执行" + i + "次");
//将子线程合并到主线程中
try {
userThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5 守护线程
调用线程对象的方法 setDaemon(true)
,可以将其设置为守护线程。
JVM的垃圾回收、内存管理等线程都是守护线程。
setDaemon(boolean on)
将该线程标记为守护线程或用户线程,该方法必须在启动线程前调用。
当正在运行的线程都是守护线程时,java虚拟机就会退出。
public class Backend extends Thread {
@Override
public void run() {
for(int i=0;i<8888;i++){
System.out.println("后端执行" + i + "次");
}
}
}
public class Front extends Thread {
@Override
public void run() {
for(int i=0;i<3;i++){
System.out.println("前端执行" + i + "次");
}
}
}
public class Test {
public static void main(String[] args) {
Backend backend = new Backend();
Front front = new Front();
//设置后台为守护线程
backend.setDaemon(true);
backend.start();
front.start();
}
}
当非守护线程执行完成后,jvm就会停止运行。
6 线程的中断和死亡
6.1 线程中断
线程中断就是让目标线程停止执行,但它不会使线程立即终止,而是给线程发送一个通知,告诉线程JVM希望退出执行,至于目标线程何时退出,完全由JVM自己决定。
public class UserThread {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
while (true){
//判断当前线程是否处于中断状态
if(this.isInterrupted()){
System.out.println(Thread.currentThread().getName() + " 处于中断状态");
break;
}
}
}
};
thread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//调用线程的中断方法
thread.interrupt();
}
}
6.2 线程死亡
线程会以如下三种方式结束,结束后就处于死亡状态:
-
run()
或 call()
方法执行完成,线程正常结束; - 线程抛出一个未捕获的
Exception
或 Error
; - 直接调用线程的
stop()
方法结束线程,该方法容易导致死锁,不建议使用。
注意:
不能对一个已经死亡的线程调用start()
方法,因为死亡线程已不再可用。
public class Test {
//线程退出标记
public static boolean exit = false;
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println("线程启动了");
while (!exit){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束了");
}
}.start();
//等待5秒,修改线程结束标记
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
exit = true;
}
}