Java 多线程核心概念与实战用法详解

Ryan
Ryan
发布时间:2026-10-13| 4人浏览过
原创

Java 多线程是并发编程的基础,掌握其创建方式、生命周期管理、线程安全控制以及线程间通信机制,对于构建高性能应用至关重要。本文将系统梳理这些核心概念并提供代码示例。

如何高效创建 Java 线程

在 Java 中,创建新执行线程主要有两种方式。无论采用哪种方式,最终都必须实例化 Thread 类或其子类来启动线程。以下是 Thread 类的常用构造函数:

  1. public Thread( );
  2. public Thread(Runnable target);
  3. public Thread(String name);
  4. public Thread(Runnable target, String name);
  5. public Thread(ThreadGroup group, Runnable target);
  6. public Thread(ThreadGroup group, String name);
  7. public Thread(ThreadGroup group, Runnable target, String name);
  8. public Thread(ThreadGroup group, Runnable target, String name, long stackSize);

第1步:继承 Thread 类覆盖 run 方法

这是最直接的线程创建方式。通过创建一个继承自 Thread 的类,并重写 run() 方法来定义线程任务。启动时需调用 start() 方法而非直接调用 run(),否则会阻塞在主线程中。

public class ThreadDemo1 {
  public static void main(String[] args){
   Demo d = new Demo();
   d.start(); // 启动线程
   for(int i=0;i<60;i++){
    System.out.println(Thread.currentThread().getName()+i);
   }
  }
}
class Demo extends Thread{
  public void run(){
   for(int i=0;i<60;i++){
    System.out.println(Thread.currentThread().getName()+i);
   }
  }
}

第2步:实现 Runnable 接口

这种方式将任务逻辑(Runnable)与执行主体(Thread)分离,更符合面向对象设计原则,也避免了单继承的限制。需先创建实现了 Runnable 接口的对象,再将其作为目标传入 Thread 构造函数。

public class ThreadDemo2 {
 public static void main(String[] args){
  Demo2 d =new Demo2();
  Thread t = new Thread(d); // 绑定任务到线程
  t.start();
  for(int x=0;x<60;x++){
   System.out.println(Thread.currentThread().getName()+x);
  }
 }
}
class Demo2 implements Runnable{
 public void run(){
  for(int x=0;x<60;x++){
   System.out.println(Thread.currentThread().getName()+x);
  }
 }
}

线程的生命周期与控制

线程从创建到销毁经历多个状态。理解并正确控制这些状态是避免死锁和资源浪费的关键。

  • 新建 (New):线程对象已创建但尚未启动。
  • 运行 (Running/Runnable):调用 start() 后进入就绪队列等待 CPU 调度,获配资源后执行 run()。
  • 阻塞/挂起 (Blocked/Waiting/Sleeping):因等待资源、睡眠或调用 wait() 而暂停。注意:suspend() 和 resume() 已废弃,因其易导致死锁,请使用 wait()/notify() 或 Lock 机制替代。
  • 终止 (Terminated):任务执行完毕或被强制中断。

关键状态控制方法:

  • start():启动线程。
    run():线程执行的入口,不可直接调用作为启动手段。
  • sleep(long millis):静态方法,使当前线程休眠指定时间,不释放锁。需处理 InterruptedException。若设置纳秒参数,JVM 可能降级为毫秒精度。
  • interrupt():中断线程,通常用于唤醒处于睡眠或等待状态的线程,抛出异常供捕获处理。
  • isAlive():判断线程是否存活(即已启动且未终止)。

线程终止的三种方式:

  1. 正常退出(推荐):通过标志位(如 boolean flag)控制循环条件,使 run() 方法自然结束。
  2. 强制停止(不推荐):stop() 方法会立即终止线程并释放所有锁,可能导致数据不一致,官方已废弃。
  3. 中断响应(推荐):使用 interrupt() 发送中断信号,线程在适当时机捕获异常或检查 isInterrupted() 状态以优雅退出。

Join 方法的作用:

join() 用于确保当前线程等待另一个线程终止后再继续执行。这常用于父线程需要获取子线程计算结果的情况,保证执行顺序的同步性。

解决多线程安全问题

当多个线程同时操作共享数据时,可能出现数据竞争。解决办法是确保同一时刻只有一个线程执行操作共享数据的代码块,即“同步”。Java 提供了两种主要同步机制:

第1步:使用同步代码块

通过 synchronized(lockObject) {...} 块来保护关键代码。锁对象可以是任意非空对象,通常建议使用专门的私有对象作为锁,以避免与外部逻辑冲突。以下示例演示了多窗口售票场景中使用同步代码块防止超卖:

class Ticket implements Runnable{
 private int ticket =400;
 public void run(){
  while(true){
   synchronized (this) { // 使用 this 作为锁
    try {
     Thread.sleep(1);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    if(ticket>0)
     System.out.println(Thread.currentThread().getName()+"---卖出"+ticket--);
    else
     break;
   }
  }
 }
}

第2步:使用同步方法

直接在方法签名上添加 synchronized 关键字。如果是实例方法,锁默认为 this;如果是静态方法,锁默认为 Class 对象。此方式代码更简洁,适用于整个方法体都需要保护的场景。

class Ticket implements Runnable{
 private int ticket = 4000;
 public synchronized void saleTicket(){ // 同步方法
  if(ticket>0)
   System.out.println(Thread.currentThread().getName()+"卖出了"+ticket--);
 }
 public void run(){
  while(true){
   saleTicket();
  }
 }
}

线程间的通信:等待与唤醒

线程间通信通常涉及生产者-消费者模式。核心机制是使用 wait() 和 notify()/notifyAll()。必须注意,这两个方法必须在 synchronized 块内调用,因为它们是 Object 类的方法,且依赖于特定的监视器锁。

第1步:基础通信与问题发现

若无同步保护,线程访问共享对象属性时会出现脏读(读到一半的数据)。加入简单的 synchronized 可解决数据完整性问题,但在无锁竞争中可能轮流执行,无法体现生产消费的协作特性(即库存满时生产者等待,库存空时消费者等待)。

第2步:引入 wait/notify 机制

通过标志位和等待/唤醒机制,实现线程间的协调。只有当条件满足时才执行操作,否则线程进入等待状态释放锁,让其他线程执行。

class Goods{
   private String name;
   private int num;
   // 生产方法:同步,需加锁
   public synchronized void produce(String name){
    // 如果库存已满(标记为true),等待消费
    while(this.isFull()) {
        try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }
    }
    // 生产商品...
    this.name = name + "编号:" + num++;
    System.out.println(Thread.currentThread().getName() + "生产了: " + this.name);
    
    // 更新状态为已生产,并唤醒所有等待者(主要是消费者)
    notifyAll(); 
   }
   
   public synchronized void consume(){
    // 如果库存为空(标记为false),等待生产
    while(!this.hasStock()) {
        try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }
    }
    // 消费商品...
    System.out.println(Thread.currentThread().getName() + "消费了: " + this.name);
    
    // 更新状态为空,并唤醒所有等待者(主要是生产者)
    notifyAll();
   }
}

在上述改进的生产消费模型中,while 循环用于处理虚假唤醒(Spurious Wakeup),确保唤醒后再次检查条件是否符合。使用 notifyAll() 比 notify() 更安全,能避免特定线程一直未被唤死的死锁风险。通过这种协作,生产者和消费者能够根据库存状态自动调节执行节奏,提高系统效率。

总结

Java 多线程编程涉及复杂的并发控制。从基础的线程创建,到生命周期管理,再到关键的线程安全和线程间通信,每一步都需要谨慎处理。推荐使用 Runnable 接口解耦任务与执行,利用 synchronized 或 java.util.concurrent 包中的高级工具(如 ReentrantLock、Semaphore)来解决同步问题,并妥善运用 wait/notify 实现高效的线程协作。以上就是Java多线程详解及用法的详细内容,更多关于Java并发编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。