JAVAEE——多线程(2)

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()。

总结

  1. 线程中断是协作式通知机制,核心是设置中断标记,而非强制终止线程;

  2. 3 个核心 API:interrupt()(设标记)、isInterrupted()(查标记不清除)、interrupted()(查标记并清除);

  3. 阻塞状态(sleep/wait/join):响应中断并抛出InterruptedException,自动清除标记;

  4. 运行状态:需主动检查isInterrupted()标记,自行实现优雅退出逻辑;

  5. 异常处理时,若需后续感知中断,需手动重新设置中断标记。

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**.避免内存泄露**:若长期持有已终止线程的引用,且该引用无法被垃圾回收,可能导致内存泄露,建议线程执行完毕后及时释放无用引用;

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔1 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区5 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai6 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说6 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json