跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 多线程基础:进程线程概念与 Thread 类详解

介绍操作系统中进程与线程的基本概念及区别,涵盖进程控制块(PCB)、资源分配与调度机制。重点讲解 Java 中创建线程的四种方式(继承 Thread、实现 Runnable、匿名内部类、Lambda),以及线程的生命周期管理方法,包括 start 与 run 的区别、前台后台线程设置、休眠、中断处理及线程等待(join)。内容旨在帮助开发者理解 Java 并发编程的基础原理与核心 API 使用。

性能调优发布于 2026/3/27更新于 2026/7/1737 浏览
Java 多线程基础:进程线程概念与 Thread 类详解

1. 进程与线程

1.1 背景介绍

1950 年代,计算机系统通常是单任务的。早期计算机一次只能执行一个程序,需要人工切换。这种设计简单但效率低下。

1960 年代,多任务系统的概念开始萌芽。早期的大型机操作系统如 IBM 的 OS/360 引入了分时技术,允许多个用户同时使用计算机资源。虽然计算机实际一次只能干一件事,但靠这种'闪电式切换',用户感觉电脑在同时处理多个任务。

1970 年代,Unix 操作系统诞生,采用了多任务设计。Unix 通过进程调度和时间片轮转机制,允许多个程序并发执行。这一设计成为现代多任务系统的基础。

**单任务 (进程) 系统:**同一时间只能运行一个程序或任务,任务必须按顺序完成。用户需等待当前任务结束后才能启动新任务。系统资源由一个任务独占,缺乏并发能力,适用于简单应用场景。

**多任务 (进程) 系统:**允许同时运行多个程序或任务,通过时间片轮转或优先级调度实现并发。

  • **协同式:**应用程序需要主动释放 CPU 资源。设计简单,但稳定性较差。
  • **抢占式 (现代主流):**由操作系统强制分配资源。操作系统可以强制中断任务,确保系统响应能力,进一步提高了并发性能。现代操作系统如 Windows、Linux 均采用抢占式多任务,支持更高效的资源利用和用户体验。

1.2 进程/任务

**进程 (Process)/任务 (Task):**进程是计算机中正在运行的程序的实例 (应用程序与进程的关系近似于 Java 中类和对象的关系),是操作系统进行资源分配的基本单位。

  • 应用程序 ≈ 类的代码定义 (静态模板)
  • 进程 ≈ 类的运行时实例/对象 (动态实体) 打开任务管理器,这里显示的每一个运行中的 exe(可执行文件),就叫做进程。

当一个进程被创建的时候,操作系统会为它创建一个或多个 PCB(Process Control Block 进程控制块),类似 C 语言的结构体/Java 中的类,PCB 里面会存放该进程的各种属性:

  • **状态:**就绪状态 (进程的资源准备完成,随时可以运行)、运行状态 (正在运行)、阻塞状态 (当进程等待某个事件时暂停执行)、终止状态 (进程执行完毕或被终止,操作系统会释放进程占用的资源)。
  • **优先级:**当存在多个进程时,优先级高的进程优先执行,优先级低的进程需要等待。
  • **记账信息:**记录进程已使用的系统资源,如 CPU、内存等设备的使用情况。
  • **上下文信息:**进程在被调度执行前的状态信息,以便进程可以从上次暂停的地方继续执行。

**文件描述符表:**文件描述符表通常是一个数组或链表,每个节点对应一个文件描述符,进程通过文件描述符来访问文件,实现进程对文件的增删查改等操作。

**内存指针:**指向的空间就是该进程创建时所申请的内存空间,指明了这个空间中哪些位置存放数据,哪些位置存放指令/代码。

**PID:**进程的 id,可以认为是进程的身份标识,每个进程的 id 是独一无二的,不能重复。

1.3 进程优缺点与线程

优点:

  • **独立性:**每个进程拥有独立的地址空间和资源,一个进程崩溃不会直接影响其他进程的运行,提高了系统的稳定性。
  • **资源隔离:**操作系统为每个进程分配独立的资源 (如内存、文件描述符等),避免资源竞争和冲突,增强了安全性。

缺点:

  • **资源开销大:**创建和销毁进程需要分配独立的地址空间和资源,上下文切换涉及保存和恢复大量寄存器状态,性能损耗较高。
  • **PCB 管理:**创建进程需连接各个进程,利用链表或者其他数据结构将 PCB 连接起来;删除进程时需将该进程的 PCB 从链表中移除。

**线程 (Thread)/轻量级进程:**由操作系统调度,不用额外分配资源,是操作系统调度执行的基本单位。一个进程可以包含多个线程,所有线程共享进程的资源 (由线程控制块 TCB 管理),但每个线程拥有独立的执行栈和程序计数器。

优点:

  • **轻量级:**线程的创建、切换和销毁比进程更高效。
  • **并发执行:**多线程允许程序在同一时间内执行多个任务,提高 CPU 利用率。
  • **共享资源:**线程可以直接访问所属进程的全局变量、堆内存等。

缺点:

  • **稳定性风险:**单个线程的崩溃可能导致整个进程终止,因为线程间缺乏隔离性。
  • **资源竞争与同步问题:**线程共享同一进程的内存空间和资源,多个线程同时访问共享数据时可能引发竞争条件,导致数据不一致。必须使用锁、信号量等同步机制,增加了编程复杂度,且不当使用可能引发死锁或性能下降。

2. Java 中的线程实现:Thread 类

2.1 创建线程

构造方法如下:

1. 直接继承 Thread 类

  • **优点:**实现简单,直接重写 run() 方法即可定义线程任务。
  • **缺点:**线程逻辑与 Thread 类耦合,不利于代码复用;Java 单继承特性限制了扩展性,无法再继承其他类。
// Thread 类实现了 Runnable 接口,所以需要重写 run 方法
class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("Hello MyThread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        // 启动线程
        thread.start();
    }
}

2. 实现 Runnable 接口

  • **优点:**避免单继承限制,可同时实现其他接口或继承类;线程任务与 Thread 解耦,便于复用。
  • **缺点:**需额外创建 Thread 类实例来启动线程,代码稍显冗余。
class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("Hello MyRunnable");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        // 启动线程
        thread.start();
    }
}

3. 匿名内部类创建 Runnable 子类对象

  • **优点:**可直接访问外部类的 final 或有效 final 变量 (变量捕获)。
  • **缺点:**代码可读性较差,尤其是逻辑复杂时。
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            // 匿名内部类
            @Override
            public void run() {
                while (true) {
                    System.out.println("Hello Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
    }
}

4. lambda 表达式创建 Runnable 子类对象

  • **优点:**可直接访问外部类的 final 或有效 final 变量 (变量捕获)。
  • **缺点:**仅适用于接口中只有一个抽象方法的情况。
public class ThreadDemo {
    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();
    }
}

2.2 获取线程名字和 id

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {});
        // 获取线程 id
        System.out.println(thread.getId());
        // 获取线程 name
        System.out.println(thread.getName());
    }
}

2.3 判断线程是否存活

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        // false, 线程还没启动
        System.out.println(thread.isAlive());
        thread.start();
        // true, 线程正在执行任务
        System.out.println(thread.isAlive());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // false, 线程结束
        System.out.println(thread.isAlive());
    }
}

2.4 前台/后台线程

**前台线程:**会阻止进程终止,即使主线程执行完毕,只要存在前台线程仍在运行,进程会继续运行直到所有前台线程完成任务。默认情况下,主线程是前台线程,通过 Thread 类创建的线程默认也是前台线程。

**后台线程:**不会阻止进程终止,当所有前台线程结束时,后台线程会被自动终止,无论是否完成任务。

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(1000);
                    System.out.println("Hello Thread");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // 把 thread 设置为后台线程,不会阻止主线程的结束,thread 线程会伴随 main 线程的结束而结束
        thread.setDaemon(true);
        thread.start();
        System.out.println("Hello Main");
    }
}

**运行结果:**Hello Main

2.5 详解 start 和 run

**start 方法:**用于启动一个新线程,新线程会执行 run 方法中的代码。每个线程只能调用一次 start,重复调用会抛出 IllegalThreadStateException (无论该线程是否执行完任务)。

public class ThreadDemo {
    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(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // 开启新线程,thread 线程执行 run 方法
        thread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("Hello Main");
            Thread.sleep(100);
        }
    }
}

**run 方法:**直接调用 run 方法不会创建新线程,而是在当前线程中执行 run 方法的代码。

public class ThreadDemo {
    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(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // main 线程执行 run 方法
        thread.run();
        for (int i = 0; i < 5; i++) {
            System.out.println("Hello Main");
            Thread.sleep(100);
        }
    }
}

2.6 线程休眠

**Thread.sleep:**是 Java 中用于暂停当前线程执行的一个静态方法。它允许线程在指定的时间内进入休眠状态,期间不会占用 CPU 资源。休眠结束后,线程会重新进入就绪 (RUNNABLE) 状态,等待调度执行。

// 毫秒级精确度,需要处理 InterruptedException(编译时异常)
public static native void sleep(long millis) throws InterruptedException;
// 纳秒级精确度
public static void sleep(long millis, int nanos) throws InterruptedException;

**知识回顾:**main 方法中:既可以 throws 声明异常,也可以 try-catch 异常。 run 方法中:只能 try-catch 异常,因为子类重写父类方法时不能修改父类方法的方法签名。

2.7 currentThread:获取当前线程实例

在继承 Thread 类的自定义线程类中,可以通过 this 关键字直接访问当前线程的属性和方法。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println(this.getId() + " " + this.getName());
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
    }
}

在使用 Runnable 接口或 Lambda 表达式创建线程时,无法直接使用 this 获取线程信息,因为 this 指向的是 Runnable 实例而非线程对象。应调用 Thread.currentThread() 获取当前线程的引用。

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            Thread t1 = Thread.currentThread();
            System.out.println(t1.getId() + " " + t1.getName());
        });
        thread.start();
    }
}

2.8 变量捕获

  1. **Java 的线程模型:**栈帧中的局部变量是线程私有的,这意味着每个线程都有自己的栈帧,局部变量存储在栈帧中,一个线程不得访问另一个线程中的局部变量。
  2. **变量捕获 (Variable Capture):**通常与 Lambda 表达式和匿名内部类相关,允许捕获外部作用域的变量,但这些变量必须是 final 修饰或事实 final。

结合以上两点,变量捕获机制允许一个线程获取另一个线程中的 final 变量。在上述代码中,thread 线程捕获到 main 线程中的 isQuit 变量,本质上是将 isQuit 变量拷贝到 thread 线程中,而不是直接访问 main 的栈帧 (避免因变量的生命周期而可能产生的线程安全问题)。如果允许匿名内部类或 lambda 表达式访问并修改局部变量,就会导致变量逃逸 (escape),即变量的引用或值被传递到栈帧之外造成栈中变量的内存地址泄漏,这种情况会引发严重的安全问题,因为原本被 Java 线程模型规定为线程私有的数据可能会被并发访问。

2.9 终止线程

通过修改类变量来终止线程

  • **缺点:**类变量属于整个进程的公共变量,访问时需要注意线程安全问题;类变量无法唤醒线程中的 sleep 方法。
public class ThreadDemo {
    public static boolean isQuit = false;

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!isQuit) {
                System.out.println("Hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("Over Thread");
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        isQuit = true;
    }
}

**interrupt():**是 Java 中用于中断线程的方法。它的核心作用是向目标线程发送一个中断信号,但并不会直接终止线程的执行。需要主动检查中断状态并决定如何响应中断。

  • **interrupted():**检查当前线程的中断状态,并清除中断标志 (即调用后中断状态会被重置为 false)。
  • **isInterrupted():**检查线程的中断状态,但不会清除中断标志 (即调用后不改变中断状态),仅返回当前状态。
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
            System.out.println("Over Thread");
        });
        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 终止线程,并唤醒线程中的 sleep
        thread.interrupt();
    }
}

2.10 join

**join 方法:**用于等待线程执行完成。调用某个线程的 join 方法后,当前线程会被阻塞,直到目标线程执行完毕。

方法说明
join()等待线程无限期执行完成
join(long millis)等待线程执行完成,但最多等待 millis 毫秒
join(long millis, int nanos)等待线程执行完成,但最多等待 millis 毫秒+nanos 纳秒
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("Hello Thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("Over Thread");
        });
        thread.start();
        // main 等待 thread 线程执行完毕
        thread.join();
        // main 线程恢复执行
        System.out.println("Hello Main");
    }
}

执行结果: Hello Thread Hello Thread Hello Thread Over Thread Hello Main

目录

  1. 1. 进程与线程
  2. 1.1 背景介绍
  3. 1.2 进程/任务
  4. 1.3 进程优缺点与线程
  5. 2. Java 中的线程实现:Thread 类
  6. 2.1 创建线程
  7. 2.2 获取线程名字和 id
  8. 2.3 判断线程是否存活
  9. 2.4 前台/后台线程
  10. 2.5 详解 start 和 run
  11. 2.6 线程休眠
  12. 2.7 currentThread:获取当前线程实例
  13. 2.8 变量捕获
  14. 2.9 终止线程
  15. 2.10 join
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 开源大模型重塑企业 AI 应用:16 个落地案例分析
  • OpenClaw 多智能体路由与飞书多机器人配置指南
  • Java 多线程实战:状态、操作与同步机制
  • 错误定位 Prompt 设计:利用 AI 快速解析异常堆栈
  • Dify 前后端分离开发:独立启动前端 Docker 容器方案
  • C++ XML 处理库 PugiXML 使用指南与实战
  • OpenClaw 配置飞书机器人完整指南
  • verl 框架在客服机器人训练与部署中的实践
  • Python量化可视化:从K线到回测的实用选择
  • AI 对话式 PCB 设计工具实战:从需求到布局的自动化流程
  • 5 款设计师必备的 Stable Diffusion 模型,涵盖 3D、电商与 B 端场景
  • Zotero 本地 AI 文献研究助手:支持 Claude Desktop 与 MCP 集成
  • Llama-3.2V-11B-cot 部署实战:视觉权重加载 Bug 修复与验证
  • 飞算 JavaAI:本地化智能代码生成与效能提升实践
  • 无人机远程路径规划技术:A*算法与 GPS 定位实现
  • 论文 AIGC 检测原理与降重工具实测指南
  • Java 8 Lambda 表达式与 Stream 流实战解析
  • 算法优选:位运算实战技巧
  • Verilog 入门:语法、仿真与 FPGA 实战
  • TongWEB 前后端应用部署步骤

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online