Java 中的多线程

2025 年 5 月 7 日 | 阅读 8 分钟

Java 中的多线程是指同时执行多个线程的过程。

线程是轻量级的子进程,是处理的最小单元。多进程和多线程都用于实现多任务处理。

然而,我们使用多线程而不是多进程,因为线程使用共享内存区域。它不分配单独的内存区域,因此可以节省内存,并且线程之间的上下文切换比进程花费的时间少。

Java 多线程主要用于游戏、动画等。

Java 多线程的优点

  1. 不会阻塞用户,因为线程是独立的,您可以同时执行多个操作。
  2. 您可以同时执行许多操作,从而节省时间。
  3. 线程是独立的,因此如果一个线程发生异常,不会影响其他线程。
  4. 多线程可以显著提高应用程序的性能,通过更好地利用多个 CPU 核心。每个线程都可以在单独的核心上运行,从而实现并行处理和更快的任务执行。
  5. 在具有图形用户界面 (GUI) 的应用程序中,多线程有助于保持界面的响应性。耗时任务可以在后台由单独的线程处理,防止主线程冻结并改善用户体验。
  6. 同一进程中的线程共享相同的内存和资源,这使得它们之间共享数据更加容易,而无需复杂的进程间通信机制。
  7. 使用多个线程可以简化复杂应用程序的设计,从而实现更模块化、更易于管理的代码。例如,可以为不同的任务分配单独的线程,例如网络通信、文件 I/O 和计算。
  8. 多线程允许应用程序在等待 I/O 操作完成时继续处理。它能够更好地利用 CPU 资源,因为线程可以在 I/O 等待期间执行有用的工作。

多任务处理

多任务处理是同时执行多个任务的过程。我们使用多任务处理来利用 CPU。多任务处理可以通过两种方式实现

  • 基于进程的多任务处理(多进程)
  • 基于线程的多任务处理(多线程)

1) 基于进程的多任务处理(多进程)

  • 每个进程都有内存地址。换句话说,每个进程分配一个单独的内存区域。
  • 进程是重量级的。
  • 进程间通信成本很高。
  • 从一个进程切换到另一个进程需要一些时间来保存和加载寄存器、内存映射、更新列表等。

2) 基于线程的多任务处理(多线程)

  • 线程共享相同的地址空间。
  • 线程是轻量级的。
  • 线程间的通信成本很低。

注意:每个线程至少需要一个进程。

Java 中的线程是什么?

线程是轻量级的子进程,是处理的最小单元。它是一个单独的执行路径。

线程是独立的。如果一个线程发生异常,它不会影响其他线程。它使用共享内存区域。

Java Multithreading

如上图所示,线程在进程内执行。线程之间存在上下文切换。OS 中可以有多个进程,一个进程可以有多个线程。

注意:一次只能执行一个线程。

Java Thread 类

Java 提供了Thread 类来实现线程编程。Thread 类提供了构造函数和方法来创建和操作线程。Thread 类继承自 Object 类并实现 Runnable 接口。

线程状态

新建 (New): 已创建但尚未开始的线程。

可运行或运行 (Runnable or Running): 已准备好运行并等待 CPU 时间的线程。

等待 (Waiting): 无限期地等待另一个线程执行特定操作的线程。

终止 (Terminated): 已完成其任务的线程。

Java Thread 类方法

修饰符和类型方法描述
voidstart()用于启动线程的执行。
voidrun()用于为线程执行操作。
static voidsleep()使线程休眠指定的时长。
静态 ThreadcurrentThread()返回当前正在执行的线程对象的引用。
voidjoin()等待线程终止。
intgetPriority()返回线程的优先级。
voidsetPriority()更改线程的优先级。
StringgetName()返回线程的名称。
voidsetName()更改线程的名称。
longgetId()返回线程的 ID。
booleanisAlive()测试线程是否处于活动状态。
static voidyield()使当前正在执行的线程对象暂停,并允许其他线程暂时执行。
voidsuspend()用于挂起线程。
voidresume()用于恢复被挂起的线程。
voidstop()用于停止线程。
voiddestroy()用于销毁线程组及其所有子组。
booleanisDaemon()测试线程是否为守护线程。
voidsetDaemon()将线程标记为守护线程或用户线程。
voidinterrupt()中断线程。
booleanisinterrupted()测试线程是否已被中断。
static booleaninterrupted()测试当前线程是否已被中断。
static intactiveCount()它返回当前线程所在线程组中的活动线程数。
voidcheckAccess()确定当前运行的线程是否具有修改线程的权限。
static booleanholdLock()当且仅当当前线程持有指定对象的监视器锁时,返回 true。
static voiddumpStack()用于将当前线程的堆栈跟踪打印到标准错误流。
StackTraceElement[]getStackTrace()返回一个堆栈跟踪元素数组,表示线程的堆栈转储。
static intenumerate()用于将每个活动线程的线程组及其子组复制到指定的数组中。
Thread.StategetState()用于返回线程的状态。
ThreadGroupgetThreadGroup()用于返回此线程所属的线程组
StringtoString()用于返回此线程的字符串表示形式,包括线程的名称、优先级和线程组。
voidnotify()用于向正在等待特定对象的单个线程发出通知。
voidnotifyAll()用于向正在等待特定对象的​​所有线程发出通知。
voidsetContextClassLoader()为 Thread 设置上下文类加载器。
类加载器getContextClassLoader()返回线程的上下文类加载器。
静态 Thread.UncaughtExceptionHandlergetDefaultUncaughtExceptionHandler()返回当线程因未捕获的异常而异常终止时调用的默认处理程序。
static voidsetDefaultUncaughtExceptionHandler()设置当线程因未捕获的异常而异常终止时调用的默认处理程序。

Java 多线程示例

示例

编译并运行

输出

Thread 3 - Count: 1
Thread 1 - Count: 1
Thread 2 - Count: 1
Thread 1 - Count: 2
Thread 2 - Count: 2
Thread 3 - Count: 2
Thread 1 - Count: 3
Thread 2 - Count: 3
Thread 3 - Count: 3
Thread 1 - Count: 4
Thread 2 - Count: 4
Thread 3 - Count: 4
Thread 1 - Count: 5
Thread 2 - Count: 5
Thread 3 - Count: 5
Thread 2 finished.
Thread 1 finished.
Thread 3 finished.
All threads have finished.

说明

通过扩展 Thread 类并覆盖其 run 方法,以每秒一次的延迟打印消息五次,此 Java 程序演示了多线程。

在主类 Main 中创建并启动了三个 MyThread 实例,使它们能够并发运行。为了确保主线程在发出所有线程已完成的最终消息之前等待所有线程完成,使用了 join() 方法。

Java 多线程的优点

  1. 提高性能:多线程支持多任务的并发执行,这可以减少执行时间并提高整体效率。
  2. 并行执行:文件处理、网络操作和计算等任务可以同时发生,从而提高应用程序的速度。
  3. 提高 GUI 响应性:对于具有图形用户界面 (GUI) 的应用程序,多线程可以通过将用户输入与后台任务分开管理来促进平滑的交互。
  4. 更好的 CPU 利用率:线程可以并行运行,从而优化可用 CPU 资源的使用,尤其是在多核处理器上。
  5. 减少开发时间:多线程将复杂任务分解为更小、可管理的线程,从而减少开发所需的精力。

Java 多线程的缺点

  1. 复杂性:与单线程系统相比,多线程应用程序更难编写、测试和调试。
  2. 资源开销:由于创建了过多的线程,性能可能会因过多的内存利用和 CPU 上下文切换而受到影响。
  3. 同步问题:共享资源管理不当可能导致竞态条件、死锁和数据不一致等问题。
  4. 错误处理困难:一个线程中的错误可能不会轻易传播到其他线程,这使得识别和解决问题更加困难。
  5. 行为不可预测:如果线程没有正确同步,CPU 调度程序(控制线程执行顺序)可能会导致意外结果。

Java 多线程多选题

1. 在 Java 中,以下哪种方式不是创建线程的有效方式?

  1. 继承 Thread 类
  2. 实现 Runnable 接口
  3. 实现 Callable 接口
  4. 实现 Serializable 接口

答案:4)

解释:Serializable 与线程无关。线程可以通过扩展 Thread、实现 Runnable 或使用 Callable 和 FutureTask 来创建。


2. Java 中的线程是什么?

  1. 程序内独立运行的轻量级进程
  2. 与主程序并发运行的代码块
  3. 可以异步调用的方法
  4. 扩展 Thread 类的 Java 类

答案:1)

解释:线程是轻量级进程,是处理的最小单元。它是一个单独的执行路径。线程是独立的。如果一个线程发生异常,它不会影响其他线程。它使用共享内存区域。


3. 以下哪个方法用于在 Java 中启动新线程?

  1. execute()
  2. start()
  3. run()
  4. new()

答案:2)

解释:start() 方法用于在 Java 中启动新线程。


4. 以下哪个是正确的线程状态顺序?

  1. 新建、运行、阻塞、终止
  2. 运行、新建、阻塞、终止
  3. 新建、运行、等待、终止
  4. 运行、阻塞、等待、终止

答案:3)

解释:Java 线程生命周期中的线程状态顺序是:新建 (New)、可运行 (Runnable)(或运行 (Running))、等待 (Waiting)、终止 (Terminated)。


5. 调用 wait() 方法时会发生什么?

  1. 它会通知其他线程开始执行。
  2. 它会立即终止。
  3. 它会等待,直到另一个线程在同一对象上调用 notify() 或 notifyAll()。
  4. 它会进入忙等待循环。

答案:3)

解释:当一个线程调用 wait() 方法时,它会等待,直到另一个线程在同一对象上调用 notify() 或 notifyAll() 来唤醒它。