Thread类基本用法
1.线程创建
1:继承java.lang.Thread类
这是最直观的线程创建方式,通过继承 Thread 类并重写其run()方法(线程执行体)来实现。
实现步骤
1.自定义线程类,继承Thread类;
2.重写Thread类的run()方法,编写线程需要执行的业务逻辑;
3.创建自定义线程类的实例;
4.调用实例的start()方法启动线程(注意:不能直接调用 run () 方法,否则只是普通方法调用,不会创建新线程)。
java
// 1. 自定义线程类,继承Thread类
class MyThread extends Thread {
// 2. 重写run()方法,线程执行体
@Override
public void run() {
// 线程要执行的业务逻辑
for (int i = 0; i < 5; i++) {
System.out.println("继承Thread类的线程:" + Thread.currentThread().getName() + ",计数:" + i);
try {
// 线程休眠100毫秒,模拟耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 测试类
public class ThreadCreateDemo1 {
public static void main(String[] args) {
// 3. 创建自定义线程实例
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
// 设置线程名称
thread1.setName("线程1");
thread2.setName("线程2");
// 4. 调用start()方法启动线程
thread1.start();
thread2.start();
// 主线程逻辑
for (int i = 0; i < 5; i++) {
System.out.println("主线程:" + Thread.currentThread().getName() + ",计数:" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.实现java.lang.Runnable接口
这是实际开发中更推荐的方式,解决了继承Thread类的单继承限制问题,同时便于实现资源共享。
实现步骤
1.自定义任务类,实现Runnable接口;
2.实现接口中的run()方法,编写线程执行的业务逻辑;
3.创建自定义Runnable任务实例;
4.将Runnable任务实例作为参数,传入Thread类的构造器,创建Thread线程实例;
5.调用Thread实例的start()方法启动线程。
java
/**
* 通过Thread真正创建线程,
* 线程里需要干啥,通过Runnable来表示
*/
/**
* 目的是让任务本身和线程这个概念能够解耦合,
* 从而后续如果变更代码,采用Runnable这样的方案,代码的修改会更简单
*/
public class Demo2_runnable {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.run();
}
public static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Hello world");
}
}
}
3.使用lambda表达式
最为常见的一种写法,与上述第1种创建方法思想一致,只是简化了它的语法
java
public class Demo5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
while (true){
System.out.println("Hello");
}
});
thread.start();
while (true){
System.out.println("AAA");
}
}
}
2.线程中断
1.中断的本质核心
Java 线程中断不是强制终止线程,而是给目标线程设置一个「中断标记」(布尔类型),表示 "当前线程收到了中断请求"。线程是否响应这个请求,完全由线程内部的业务逻辑决定(可选择立即退出、执行完当前任务后退出,或忽略中断),这是一种安全的协作式机制。
2.线程中断的核心API
1.interrupt(),实例方法,给目标线程设置**中断标记,**触发中断通知
2.isInterrupted(),实例方法,检查当前线程的中断标记状态
3.interrupted(),静态方法,检查当前线程中断标记状态,同时清除中断标记
java
public class ThreadInterruptBlockDemo {
public static void main(String[] args) throws InterruptedException {
// 创建一个处于sleep阻塞的线程
Thread blockThread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + ":进入睡眠阻塞,时长2000毫秒");
// 阻塞状态:sleep()会响应中断,抛出InterruptedException
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ":睡眠正常结束(未被中断)");
} catch (InterruptedException e) {
// 捕获异常:此时中断标记已被自动清除(变为false)
System.out.println(Thread.currentThread().getName() + ":捕获到中断异常,当前中断标记:" + Thread.currentThread().isInterrupted());
// 可选:重新设置中断标记,让后续逻辑感知到中断
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName() + ":重新设置中断标记,当前中断标记:" + Thread.currentThread().isInterrupted());
// 优雅退出线程(核心:响应中断,终止任务)
return;
}
}, "阻塞线程");
// 启动线程
blockThread.start();
// 主线程休眠500毫秒,确保blockThread进入sleep状态
Thread.sleep(500);
// 给blockThread发送中断请求
System.out.println("主线程:给阻塞线程发送中断请求");
blockThread.interrupt();
// 等待blockThread执行完成
blockThread.join();
System.out.println("主线程:程序执行结束");
}
}
3.关键注意事项
1.中断不是强制终止 :线程是否退出完全由自身逻辑决定,避免了强制终止导致的资源未释放(如文件流、数据库连接未关闭)问题;
2.InterruptedException 异常处理 :捕获该异常后,中断标记会被清除,若后续逻辑需要感知中断,需手动调用Thread.currentThread().interrupt()重新设置标记;
3.避免忽略中断 :不要捕获InterruptedException后不做任何处理,否则线程无法响应中断,违背中断机制的设计初衷;
4.静态方法 interrupted () 的慎用:该方法会清除中断标记,若需要保留标记状态,应使用实例方法isInterrupted()。
总结
-
线程中断是协作式通知机制,核心是设置中断标记,而非强制终止线程;
-
3 个核心 API:interrupt()(设标记)、isInterrupted()(查标记不清除)、interrupted()(查标记并清除);
-
阻塞状态(sleep/wait/join):响应中断并抛出InterruptedException,自动清除标记;
-
运行状态:需主动检查isInterrupted()标记,自行实现优雅退出逻辑;
-
异常处理时,若需后续感知中断,需手动重新设置中断标记。
3.线程等待
线程等待的核心是让一个线程暂停执行(进入阻塞状态),等待特定条件满足(如其他线程执行完毕、资源就绪)后再恢复执行,用于协调多个线程的执行顺序或解决资源竞争问题。
1.核心方法------Thread.join
join() 是 Thread 类的实例方法,核心作用是:让调用 join() 方法的当前线程(如主线程)阻塞,直到被调用 join() 的目标线程(如子线程)执行完成(终止)后,当前线程才会恢复执行。适用于需要按顺序执行线程的场景(如主线程等待子线程计算完成后,获取子线程的执行结果)。
核心特性
支持超时等待:提供 join(long millis)(毫秒级)、join(long millis, int nanos)(纳秒级)重载方法,超过指定时间后,当前线程不再阻塞,直接恢复执行;
无参 join() 等价于 join(0),表示无限等待,直到目标线程终止;
可响应中断:当阻塞线程被调用 interrupt() 时,会抛出 InterruptedException 异常,并自动清除中断标记;
底层依赖 Object.wait() 实现,无需手动获取锁,使用简单。
java
public class ThreadJoinDemo {
public static void main(String[] args) {
// 目标线程1:模拟耗时任务(计算1到5的累加)
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":开始执行耗时任务");
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
try {
Thread.sleep(200); // 模拟任务耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ":任务执行完成,累加和为:" + sum);
}, "子线程1");
// 目标线程2:模拟简单任务
Thread thread2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":开始执行简单任务");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":简单任务执行完成");
}, "子线程2");
// 启动目标线程
thread1.start();
thread2.start();
try {
System.out.println(主线程:开始等待子线程1执行完毕");
// 主线程调用thread1.join(),阻塞直到thread1执行完成
thread1.join();
System.out.println("主线程:子线程1执行完毕,开始等待子线程2(超时1000毫秒)");
// 主线程等待thread2,最多等待1000毫秒(此处thread2早已执行完成,直接恢复)
thread2.join(1000);
} catch (InterruptedException e) {
System.out.println("主线程:等待被中断");
e.printStackTrace();
}
System.out.println("主线程:所有子线程(或超时)执行完毕,主线程继续执行");
}
}
2.核心方法------object.wait()/notify()/notifyAll()
wait() 是 Object 类的实例方法(所有 Java 对象都拥有),核心作用是:让当前线程释放对象锁,进入该对象的等待队列,阻塞等待其他线程唤醒(或超时)。
核心特性
- 必须在同步环境中调用:
wait()/notify()/notifyAll()必须在synchronized同步代码块或同步方法中执行(需先获取该对象的监视器锁),否则会抛出IllegalMonitorStateException异常; - 释放对象锁:线程调用
wait()后,会立即释放持有的该对象锁(与Thread.sleep()不同,sleep()不会释放锁),让其他线程有机会获取锁; - 唤醒后需重新竞争锁:被
notify()/notifyAll()唤醒的线程,不会立即恢复执行,而是需要重新竞争对象锁,获取锁后才会继续执行wait()之后的代码; - 支持超时等待:wait(long millis) / wait(long millis, int nanos) 重载方法,超时后线程自动唤醒,无需其他线程通知;
- 避免虚假唤醒:线程可能会无原因地被唤醒(虚假唤醒),因此必须在 循环中 判断等待条件,而非 if 判断。
关键方法
1.wait(),当前线程释放锁,无限期阻塞,直到其他线程调用该对象的 notify()/notifyAll() 唤醒
2.notify(),唤醒该对象等待队列中的 一个随机线程,使其进入锁竞争队列
3.notifyAll(),唤醒该对象等待队列中的 所有线程,使其进入锁竞争队列
以使用三个线程打印ABC为例
java
public static void main(String[] args) throws InterruptedException {
Object object1 = new Object();
Object object2 = new Object();
Object object3 = new Object();
Thread t1 = new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
synchronized (object1){
object1.wait();
}
Thread.sleep(1000);
System.out.print("A");
synchronized (object2){
object2.notify();
}
}
}catch (InterruptedException e){
throw new RuntimeException();
}
});
Thread t2 = new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
synchronized (object2){
object2.wait();
}
Thread.sleep(1000);
System.out.print("B");
synchronized (object3){
object3.notify();
}
}
}catch (InterruptedException e){
throw new RuntimeException();
}
});
Thread t3 = new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
synchronized (object3){
object3.wait();
}
Thread.sleep(1000);
System.out.print("C");
System.out.println();
synchronized (object1){
object1.notify();
}
}
}catch (InterruptedException e){
throw new RuntimeException();
}
});
t1.start();
t2.start();
t3.start();
Thread.sleep(100);
synchronized (object1){
object1.notify();
}
}
3.注意事项
1.join() 异常处理:调用 join() 时必须捕获 InterruptedException ,避免线程阻塞时无法响应中断;
2.wait() 必在同步环境 :遗漏 synchronized 会抛出 IllegalMonitorStateException,这是高频错误;
3.虚假唤醒处理:wait() 必须配合 while 循环判断等待条件,不能用 if(if 仅判断一次,虚假唤醒后会直接执行后续逻辑,导致错误);
4.wait() 与 sleep() 区别:wait()释放锁 、需同步环境、用于线程协作;sleep() 不释放锁、无调用环境限制、用于简单延时。
4.线程休眠
线程休眠的核心是通过 Thread.sleep() 静态方法实现,用于让当前执行的线程暂停执行指定时长,进入阻塞状态,待休眠时间结束后自动恢复到就绪状态(并非立即执行,需等待 CPU 调度)。
1.核心本质
Thread.sleep() 是 Java 提供的线程延时工具,其核心作用是:强制当前线程放弃 CPU 执行权,进入 "休眠阻塞" 状态,在指定的时间内,该线程不会参与 CPU 的调度竞争。当休眠时间耗尽,线程会自动从阻塞状态切换到就绪状态,等待 CPU 分配执行时间后,才会继续执行后续代码。
2.核心方法------Thread.sleep()
Thread.sleep() 是 java.lang.Thread 类的静态方法,仅作用于当前线程(即调用该方法时所在的线程),提供两种重载形式
1.public static void sleep(long millis) throws InterruptedException,休眠指定毫秒数(millis ≥ 0,若为 0 则等同于让线程放弃当前 CPU 时间片,立即进入就绪态)
2.public static void sleep(long millis, int nanos) throws InterruptedException,休眠毫秒数 + 纳秒数(nanos 范围:0~999999),实际精度受操作系统时钟影响,通常无需使用
关键说明
静态方法特性:不能通过线程实例调用(语法上允许 thread.sleep(),但实际仍作用于当前线程,非目标线程,易误导,不推荐);
检查异常:两种方法均抛出 InterruptedException,必须捕获或声明抛出(线程休眠期间若被中断,会触发该异常并终止休眠)。
核心特性
1.不释放对象监视器锁(关键区别于 Object.wait())
线程调用 sleep() 后,只会放弃 CPU 执行权,不会释放任何已持有的对象锁(synchronized 锁)。这意味着,若线程在同步代码块 / 同步方法中休眠,其他线程将无法获取该对象的锁,只能等待休眠线程唤醒并执行完同步逻辑后,才有机会竞争锁。
2.响应线程中断
线程休眠期间,若其他线程调用该线程的 interrupt() 方法,休眠线程会立即被唤醒,并抛出 InterruptedException 异常,同时自动清除线程的中断标记。
3.休眠时间是 "最小延时",非精确时间
sleep() 指定的时间是线程休眠的最短时间,线程唤醒后不会立即执行,而是进入就绪态等待 CPU 调度。因此,线程实际恢复执行的时间可能会超过指定的休眠时间,具体取决于 CPU 的繁忙程度。
4.不改变线程的中断状态(非异常场景)
若线程未被中断,休眠正常结束后,线程的中断标记状态不会发生变化。
3.注意事项
1.避免错误调用实例方法 :不要通过 new Thread().sleep() 形式调用(语法合法但逻辑错误),该方法仍作用于当前线程,非创建的线程实例;
2.休眠时间非精确值 :由于线程唤醒后需等待 CPU 调度,实际恢复执行的时间可能大于指定的休眠时间;
3.中断异常的处理:休眠期间线程被中断会抛出 InterruptedException,捕获该异常后,中断标记会被自动清除,若后续需感知中断,需手动重新设置;
4.不释放锁的风险:在同步环境中使用 sleep() 时,若休眠时间过长,会导致其他线程长期无法获取锁,影响程序性能;
5.获取线程实例
1.核心方式------Thread.currentThread()-获取当前正在执行的线程实例
Thread.currentThread() 是 Thread 类的静态方法 ,核心作用是:获取当前正在执行这段代码的线程实例(即当前线程自身)。这是最常用的获取线程实例的方式,适用于需要在线程内部获取自身引用、查询线程信息(名称、状态、优先级等)的场景。
核心特性
静态方法:直接通过 Thread.currentThread() 调用,无需依赖线程实例;
实时性:始终返回当前执行代码的线程,无论在主线程还是子线程中调用,都能准确获取当前线程引用;
通用性:可在任何代码块中调用(无调用环境限制),包括同步方法、普通方法、Lambda 表达式等。
2.注意事项
1.Thread.currentThread() 的核心用途 :该方法是线程内部获取自身实例的唯一方式,尤其在匿名线程(Lambda、匿名内部类)中,无法直接持有引用,只能通过该方法获取当前线程;
2.线程引用的有效性:线程执行完毕(状态变为TERMINATED)后,其实例引用仍有效,可通过引用查询线程的最终状态、ID 等信息,但无法再次启动(start()方法只能调用一次);
3**.避免内存泄露**:若长期持有已终止线程的引用,且该引用无法被垃圾回收,可能导致内存泄露,建议线程执行完毕后及时释放无用引用;