引言

Java 多线程与并发编程是中高级开发者必须掌握的核心技能之一。随着多核 CPU 的普及,如何高效利用多线程处理任务成为提升系统性能的关键。本文将从基本概念、常用类库,到实战案例,图文并茂地讲解 Java 并发编程的核心知识。


一、多线程与并发的基本概念

1.1 什么是线程

线程(Thread) 是程序执行的最小单位,是进程中的一个执行路径。一个进程可以包含多个线程,它们共享进程的内存空间。

📌 图示:进程与线程关系

代码语言:javascript

AI代码解释

lua复制编辑+----------------------+
|      进程(Process) |
|  +----------------+  |
|  | 线程1(主线程) |  |
|  | 线程2          |  |
|  | 线程3          |  |
|  +----------------+  |
+----------------------+
1.2 并发 vs 并行
  • 并发(Concurrency):同一时间段内交替执行多个任务(单核 CPU 实现)
  • 并行(Parallelism):多个任务同时执行(多核 CPU 实现)

二、Java 创建线程的三种方式

2.1 继承 Thread 类

代码语言:javascript

AI代码解释

java复制编辑public class MyThread extends Thread {
    public void run() {
        System.out.println("线程执行:" + Thread.currentThread().getName());
    }
}

使用:

代码语言:javascript

AI代码解释

java复制编辑new MyThread().start();
2.2 实现 Runnable 接口

代码语言:javascript

AI代码解释

java复制编辑public class MyRunnable implements Runnable {
    public void run() {
        System.out.println("实现 Runnable:" + Thread.currentThread().getName());
    }
}

使用:

代码语言:javascript

AI代码解释

java复制编辑new Thread(new MyRunnable()).start();
2.3 使用 Callable 接口 + Future

可以有返回值、可抛出异常:

代码语言:javascript

AI代码解释

java复制编辑Callable<Integer> task = () -> {
    return 42;
};
FutureTask<Integer> future = new FutureTask<>(task);
new Thread(future).start();
System.out.println("结果:" + future.get());

三、线程状态与生命周期图解

线程状态(Java 中 Thread.State):

代码语言:javascript

AI代码解释

sql复制编辑+---------+      start()     +----------+
|  NEW    |----------------->| RUNNABLE |
+---------+                 /+----------+
       |                   /      |
       |          被调度 /        | 运行完毕
       |               /         v
       |         +----------+  +---------+
       +-------->| BLOCKED |  | TERMINATED
                 +----------+  +---------+

四、线程同步与锁机制

4.1 为什么需要同步?

当多个线程访问共享资源时,可能会发生线程安全问题,导致数据错误或逻辑紊乱。

例如:

代码语言:javascript

AI代码解释

java复制编辑int count = 0;

public void add() {
    count++; // 非原子操作
}
4.2 使用 synchronized 实现线程安全

代码语言:javascript

AI代码解释

java复制编辑public synchronized void add() {
    count++;
}

等价于:

代码语言:javascript

AI代码解释

java复制编辑public void add() {
    synchronized(this) {
        count++;
    }
}

📌 加锁范围建议控制精确,避免性能瓶颈。


五、常用并发工具类(JUC)

Java 提供 java.util.concurrent 工具包,简称 JUC,非常强大。

5.1 CountDownLatch

多个线程执行完再继续下一步:

代码语言:javascript

AI代码解释

java复制编辑CountDownLatch latch = new CountDownLatch(3);

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        System.out.println("线程完成:" + Thread.currentThread().getName());
        latch.countDown();
    }).start();
}

latch.await(); // 主线程等待
System.out.println("全部完成");
5.2 CyclicBarrier

多个线程到达某个点后一起执行:

代码语言:javascript

AI代码解释

java复制编辑CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程准备就绪,开始执行");
});

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        System.out.println("线程准备好:" + Thread.currentThread().getName());
        try {
            barrier.await();
        } catch (...) {}
    }).start();
}
5.3 Semaphore(信号量)

控制并发数量(限流):

代码语言:javascript

AI代码解释

java复制编辑Semaphore semaphore = new Semaphore(2); // 最多2个线程同时执行

for (int i = 0; i < 5; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire();
            System.out.println("访问:" + Thread.currentThread().getName());
            Thread.sleep(1000);
            semaphore.release();
        } catch (...) {}
    }).start();
}

六、线程池机制详解

6.1 为什么使用线程池?
  • 重用线程,避免频繁创建销毁
  • 管理线程生命周期
  • 控制最大线程数,提高系统稳定性
6.2 使用 Executors 创建线程池

代码语言:javascript

AI代码解释

java复制编辑ExecutorService pool = Executors.newFixedThreadPool(3);
pool.execute(() -> System.out.println("任务执行"));

📌 图示:线程池组成结构

代码语言:javascript

AI代码解释

rust复制编辑任务提交 -> 阻塞队列 -> 工作线程池 -> 执行任务
                    ↘ 拒绝策略处理
6.3 自定义线程池参数

代码语言:javascript

AI代码解释

java复制编辑ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2, 5, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

七、实战案例:并发爬虫框架简化版

场景:使用多线程爬取多个网页数据
7.1 任务类定义

代码语言:javascript

AI代码解释

java复制编辑class CrawlerTask implements Runnable {
    private String url;
    public CrawlerTask(String url) {
        this.url = url;
    }

    public void run() {
        System.out.println("开始爬取:" + url);
        // 模拟耗时任务
        try { Thread.sleep(2000); } catch (Exception e) {}
        System.out.println("完成:" + url);
    }
}
7.2 使用线程池爬取多个页面

代码语言:javascript

AI代码解释

java复制编辑ExecutorService executor = Executors.newFixedThreadPool(5);
List<String> urls = List.of("http://a.com", "http://b.com", "http://c.com");

for (String url : urls) {
    executor.submit(new CrawlerTask(url));
}
executor.shutdown();

八、多线程开发的常见问题及排查技巧

8.1 死锁

线程 A 等待 B 的资源,B 又等待 A:

代码语言:javascript

AI代码解释

java复制编辑// 避免死锁的建议:按固定顺序加锁,避免嵌套锁
8.2 线程泄漏

线程未释放、未关闭线程池导致内存占用过高。

✅ 建议使用 shutdown() 正确关闭线程池。


九、新特性推荐:虚拟线程(Project Loom)

Java 21 引入虚拟线程(Virtual Thread):

代码语言:javascript

AI代码解释

java复制编辑Thread.startVirtualThread(() -> {
    System.out.println("虚拟线程执行");
});
  • 启动代价小(百万级线程)
  • 用法接近传统线程
  • 不再强依赖线程池

🌟 是未来并发编程方向,值得关注!


总结

Java 多线程与并发编程涉及底层原理、工具类、线程池管理以及实战技巧,是深入 Java 后端开发的核心。通过掌握:

  • 多线程创建与状态管理
  • 锁机制与并发控制类
  • 线程池的合理使用
  • 实际应用案例实现

你将具备开发高并发、高性能系统的能力。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐