目录
[一、 Thread类及常见方法](#一、 Thread类及常见方法)
[1.1 Thread的常见构造方法](#1.1 Thread的常见构造方法)
[1.2 Thread的几个常见属性](#1.2 Thread的几个常见属性)
[1.3 后台线程](#1.3 后台线程)
[1.5 演示线程中断](#1.5 演示线程中断)
[1.5.2 通过interrupt()方法中断](#1.5.2 通过interrupt()方法中断)
[1.6 等待一个线程](#1.6 等待一个线程)
[1.7 获取当前线程](#1.7 获取当前线程)
[1.8 休眠当前线程](#1.8 休眠当前线程)
[2.1 线程的所有状态](#2.1 线程的所有状态)
[2.2 观察线程的不同状态](#2.2 观察线程的不同状态)
[2.3 打印状态](#2.3 打印状态)
我们之前已经成功创建了一个线程,使用到了Java中Thread类,详情可以看这一篇内容
JavaEE初阶------多线程(1)初识线程与创建线程-CSDN博客https://blog.csdn.net/Yoko_999/article/details/153076456?spm=1011.2124.3001.6209这一节我们具体来了解Thread类常用方法以及线程的状态
一、 Thread类及常见方法
Thread类是JVM⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的Thread对象与之关
联。
⽤我们之前在单线程学过的知识来看,每个执⾏流,会需要有⼀个对象来描述;⽽我们现在学习的线程执行流,就是由Thread对象来进行描述,**一个线程就会有一个Thread对象,JVM会将这些Thread对象组织起来,⽤于线程调度,**线程管理。
1.1 Thread的常见构造方法
|-------------------------------------|------------------------|
| 方法 | 说明 |
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用Runnable对象创建线程对象 |
| Thread(String name) | 创建线程对象并命名 |
| Thread(Runnable target,String name) | 使用Runnable对象创建线程对象,并命名 |
java
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
关于给线程的名字,首先我们要区分一下线程名字和Thread类的名字不是同一个概念,线程名字是指在操作系统上的线程名,我们可以使用我们之前提到的jconsole来观察线程名
java
public class Demo_01 {
public static void main(String[] args) {
Thread t1=new Thread(()->{
while(true){
}
});
t1.start();
}
}
我们让线程循环不退出,然后在jconsole中可以看到Thread-0,这个就是我们的线程名,而不是代码中的t1

java
public class Demo_01 {
public static void main(String[] args) {
Thread t1=new Thread(()->{
while(true){
}
},"线程1");
t1.start();
}
}
我们设置该线程的名字为"线程1",再去jconsole观察,发现确实是这个名字

1.2 Thread的几个常见属性
|--------|-----------------|
| 属性 | 获取方法 |
| ID | getId() |
| 名称 | getName() |
| 状态 | getState() |
| 优先级 | getPriority() |
| 是否后台线程 | isDaemon() |
| 是否存活 | isAlive() |
| 是否被中断 | isInterrupted() |
- 这里的ID是JVM默认为Thread对象生成的一个编号,注意这个编号是Java层面的,不是操作系统层面的,要和PCB区分开
- 状态表⽰线程当前所处的⼀个情况,也是Java层面定义的状态,和PCB区分开
- 优先级⾼的线程理论上来说更容易被调度到
- 线程分为前台线程和后台线程,通过这个标识位来区分当前线程为前台还是后台
- 是否存活,表示的是系统中的PCB是否销毁
- 是否被中断是设置一个标志位让线程在执行是判断是否要退出
我们通过一段代码来看一下线程的各个字段的具体内容
java
public class Demo_03 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
for(int i=0;i<10;i++){
try {
System.out.println(Thread.currentThread().getName()+"我还在运行");
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//退出循环
System.out.println(Thread.currentThread().getName()+"我即将运行结束");
});
//主线程
System.out.println(thread.getName()+": ID "+thread.getId());
System.out.println(thread.getName()+": 名称 "+thread.getName());
System.out.println(thread.getName()+": 状态 "+thread.getState());
System.out.println(thread.getName()+": 优先级 "+thread.getPriority());
System.out.println(thread.getName()+": 是否后台线程 "+thread.isDaemon());
System.out.println(thread.getName()+": 是否存活 "+thread.isAlive());
System.out.println(thread.getName()+": 是否被中断 "+thread.isInterrupted());
//启动线程
thread.start();
while(thread.isAlive()){
}
System.out.println(thread.getName()+": 状态 "+thread.getState());
}
}
代码中我们先运行主线程的内容,打印线程未开始之前的字段状态,然后线程开始运行,通过一个while循环等thread退出之后再打印当前状态

1.3 后台线程
我们来通过代码演示一下后台线程
java
public class Demo_04 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
while(true){
System.out.println("hello thread...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
//启动线程
thread.start();
System.out.println("线程是否存活:"+thread.isAlive());
System.out.println("main 方法执行完成");
}
}
当前线程为前台线程,我们运行看下结果

可以看到主线程中main方法只想完成,结束之后thread线程还在运行
java
public class Demo_04 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
while(true){
System.out.println("hello thread...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
//设置该进程为后台进程
thread.setDaemon(true);
//启动线程
thread.start();
System.out.println("线程是否存活:"+thread.isAlive());
System.out.println("main 方法执行完成");
}
}
我们在启动之前通过thread.setDaemon(true)把进程设置为后台进程,我们再运行来看结果
可以看到主线程结束之后,整个程序就结束了,所以我们可以理解为JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
1.4演示线程是否存活
java
public class Demo_05 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("hello thread.. ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程执行完成。");
});
System.out.println("启动之前查看线程是否存活:" + thread.isAlive());
// 启动线程
thread.start();
System.out.println("启动之后查看线程是否存活:" + thread.isAlive());
// 等待线程执行完成
thread.join();
// 保证PCB已销毁
Thread.sleep(1000);
System.out.println("线程结束之后查看线程是否存活:" + thread.isAlive());
}
}

第一个false是PCB还未创建,true代表PCB存在,第二个false代表PCB已经销毁
1.5 演示线程中断
中断就是当任务进行到一半需要停止,通过一个信号使线程退出
例如我在学习,突然一个电话来了,我需要停止学习去接电话
1.5.1通过自定义标志位中断
java
public class Demo_206 {
// 定义一个标志位
static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!isQuit) {
System.out.println("hello thread...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 线程退出
System.out.println("线程退出...");
});
// 启动线程
thread.start();
// 休眠5秒
Thread.sleep(5000);
// 修改标志位,相当于电话进来的中断信号
isQuit = true;
}
}

我们能看到休眠的五秒内线程打印了5次,然后我们修改标志位之后线程中断退出
1.5.2 通过interrupt()方法中断
我们先来学习一下中断有关的方法
|-------------------------------------|------------------|
| 方法 | 说明 |
| public void interrupt() | 中断对象关联的线程 |
| public static boolean interrupted() | 判断当前线程中断标志位是否设置 |
| public boolean isInterrupted() | 判断对象灌篮的线程标志位是否设置 |
我们解析一下代码,首先我们调用isInterrupted作为while循环的标志,每打印一句话之后就休眠1秒,然后我们在线程启动之前和运行中和中断之后查看线程是否存活
java
public class Demo_207 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
// 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
while (!Thread.currentThread().isInterrupted()) {
// 线程中具体的任务是打印一句话
System.out.println("hello thread...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程已退出");
});
System.out.println("线程是否存活:" + thread.isAlive());
// 启动线程
thread.start();
// 休眠一会
Thread.sleep(1000);
System.out.println("线程是否存活:" + thread.isAlive());
// 中断线程,发出中断信号
thread.interrupt();
// 等待线程销毁
Thread.sleep(100);
// 查看是否存活
System.out.println("线程是否存活:" + thread.isAlive());
}
}

我们来看运行结果发现,在线程运行之后报错sleep interrupted,这是因为我们这个线程每次打印之后都要休眠1秒,线程大部分时间处于休眠。在调用interrupt方法时,线程也处于休眠状态,所以报错。而且调用完中断方法之后,线程没有像我们预想的那样结束运行,而是还存活并且执行任务。
也就是说明当线程休眠时调用interrupt方法,会报错而且线程也不会被中断
那么我们就需要处理一下这种情况:当捕获到中断报错时,我们手动break退出
java
public class Demo_07 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
// 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
while (!Thread.currentThread().isInterrupted()) {
// 线程中具体的任务是打印一句话
System.out.println("hello thread...");
// 线程大部分时间在sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("休眠被中断");
// 处理中断逻辑
break;
}
}
System.out.println("线程已退出");
});
System.out.println("线程是否存活:" + thread.isAlive());
// 启动线程
thread.start();
// 休眠一会
Thread.sleep(1000);
System.out.println("线程是否存活:" + thread.isAlive());
// 中断线程,发出中断信号
thread.interrupt();
// 等待线程销毁
Thread.sleep(100);
// 查看是否存活
System.out.println("线程是否存活:" + thread.isAlive());
}
}

这样线程在中断之后也退出了,避免调用interrupt方法之后线程仍在执行
我们来看一下正常状态下调用中断方法的运行结果
java
public class Demo_08 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
// 通过线程对象内部维护的中断标识,判断当前线程是否需要中断
while (!Thread.currentThread().isInterrupted()) {
// 线程中具体的任务是打印一句话
System.out.println("hello thread...");
}
System.out.println("线程已退出");
});
System.out.println("线程是否存活:" + thread.isAlive());
// 启动线程
thread.start();
// 休眠一会
Thread.sleep(1);
System.out.println("线程是否存活:" + thread.isAlive());
// 中断线程,发出中断信号
thread.interrupt();
// 等待线程销毁
Thread.sleep(1);
// 查看是否存活
System.out.println("线程是否存活:" + thread.isAlive());
}
}
这次我们在代码中不进行休眠了,让线程任务一直处于执行状态,此时调用中断方法就可以正常中断
我们看到调用中断之后线程自动中断退出
调用interrupt()方法时:
1.如果线程在运行状态,直接中断线程,不会报异常
2.如果线程在等待状态,就会报一个中断异常,我们要在异常处理代码块中手动break进行中断逻辑实现
1.6 等待一个线程
在某些场景下,任务不可以并行运行,而是需要等待一个线程运行完成后才可以决定当前线程的行为。比如:只有等包子店的包子出炉了我才能买;只有等别人给我转账之后,我才可以存这笔钱。
|-----------------------------------------|----------------------|
| 方法 | 说明 |
| public void join() | 等待线程结束 |
| public void join(long millis) | 等待线程结束,但是最多等millis毫秒 |
| public void join(long millis,int nanos) | 同理等待线程结束,但是时间精度更高 |
java
public class Demo_09 {
public static void main(String[] args) throws InterruptedException {
Runnable target = () -> {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName()
+ ": 我还在⼯作!");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我结束了!");
};
Thread thread1 = new Thread(target, "李四");
Thread thread2 = new Thread(target, "王五");
System.out.println("先让李四开始⼯作");
thread1.start();
thread1.join();
System.out.println("李四⼯作结束了,让王五开始⼯作");
thread2.start();
thread2.join();
System.out.println("王五⼯作结束了");
}
}
我们能看到,线程1开启后我们是等到他结束之后再执行线程2
如果把join去掉之后我们看一下结果
我们能看到,如果去掉join之后,两个线程就并行了,没有等待这一行为
1.7 获取当前线程
|---------------------------------------|-------------|
| 方法 | 说明 |
| public static Thread currentThread(); | 返回当前线程对象的引用 |
1.8 休眠当前线程
|-----------------------------------------------------------------------------|-------------|
| 方法 | 说明 |
| public static void sleep(long millis),throws InterruptedException | 返回当前线程对象的引用 |
| public static void sleep(long millis,int nanos) throws InterruptedException | 更高精度控制休眠时间 |
二、线程的状态
2.1 线程的所有状态
|-------------------|-----------------------------------------------------------|
| 状态 | 说明 |
| NEW | 安表示创建好了一个Java线程对象,安排好任务,但是还未启动,没有调用start()方法之前不会创建PCB |
| RUNNABLE | 可工作的,是运行和就绪的状态,在执行任务是最常见的一种状态,在系统中有对应的PCB |
| BLOCKED | 等待锁的状态,阻塞中的一种 |
| WAITING | 等待状态,但是没有等待时间,一直死等,直到被唤醒 |
| TIMED_WAITING | 等待状态,但是指定了等待时间的阻塞状态,超过等待时间之后就不再等候 |
| TERMINATE | 完成状态,PCB已经销毁,但是Java线程对象还存在 |
2.2 观察线程的不同状态
java
public class Demo_10 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10_0000_0000l; i++) {
}
});
// 启动之前看一下线程的状态
System.out.println("启动之前 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
// 启动线程
thread.start();
// 启动之后
System.out.println("启动之后 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
// 等待线程执行完成
thread.join();
// 线程结束之后查看状态
System.out.println("结束之后 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
}
}

能看到线程调用start()方法之前处于NEW状态,安排好工作,处于即将开始工作的状态,PCB还没创建;调用start()方法之后开始运行,PCB已经创建;调用join()方法,等待完成之后处于TERMINATED结束状态,PCB已经销毁不存活。
java
public class Demo_301 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("hello thread...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 启动之前看一下线程的状态
System.out.println("启动之前 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
// 启动线程
thread.start();
// 查看状态之前等待一会
TimeUnit.MILLISECONDS.sleep(500);
// 启动之后
System.out.println("启动之后 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
// 等待线程执行完成
thread.join();
// 线程结束之后查看状态
System.out.println("结束之后 :" + thread.getState());
System.out.println("是否存活 :" + thread.isAlive());
}
}
我们把线程任务加上一个等待的操作,这样线程大部分时间处于等待的情况,我们再来查看线程状态

这样线程开始运行之后,我们查看状态,看到的就不是原来的RUNNABLE状态了,而是TIMED_WAITING状态了
2.3 打印状态
Java中的线程状态是存储在一个枚举中,我们可以打印查看一下
java
public class Demo_11 {
public static void main(String[] args) {
// 线程状态 定义在一个枚举中
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
