Java入门进阶:多线程、线程安全、线程池

Java 入门进阶:多线程、线程安全、线程池(一篇就够了)

嘿,重阳!纽约的3月周末(2026年3月7日晚9:16,估计你在家刷代码备战项目~),Java 多线程是并发编程的“入门门槛”,从简单 Thread 到线程池,是进阶必杀技。高并发时代(如 Web 服务、AI 训练),掌握它能让你的代码从“单核爬行”变“多核飞奔”。今天咱们来一场“零基础到实战”的详解,基于 JDK 8+(JUC 包核心),用代码示例、表格和流程描述,带你一步步拆解。走起!🚀

1. Java 多线程基础:为什么需要?怎么入门?

为什么多线程?:单线程串行执行,IO/计算阻塞时 CPU 闲置。多线程并行,利用多核 CPU,提高吞吐(QPS ↑)。痛点:共享资源竞争 → 线程安全问题。

核心概念

  • 进程 vs 线程:进程是资源单位(内存独立),线程是执行单位(共享进程内存,轻量)。
  • 并发 vs 并行:并发(交替执行,看似同时),并行(真同时,多核)。

创建线程方式(入门三板斧):

  1. 继承 Thread:简单,但单继承限制。
  2. 实现 Runnable:推荐,接口多实现。
  3. Callable + Future:带返回值的异步(JUC)。

表格对比(选型速记):

方式代码示例优缺点适用
继承 Thread“`java
Runnablejava<br>Runnable r = () -> System.out.println("Hello Runnable!");<br>new Thread(r).start();<br>+ 灵活;- 无返回。共享资源任务。
Callablejava<br>Callable<String> c = () -> "Result";<br>FutureTask<String> ft = new FutureTask<>(c);<br>new Thread(ft).start(); String res = ft.get();<br>+ 返回值/异常;- 需 Future 包装。异步计算。

生命周期(面试常考,画状态机):

  • NEWRUNNABLE(start()) → RUNNING(run() 执行) → BLOCKED/WAITING/TIMED_WAITING(锁/等待) → TERMINATED(结束)。
  • 示例:Thread.sleep(1000) → TIMED_WAITING;synchronized 争锁 → BLOCKED。

入门小 tip:用 lambda(JDK 8+)简化:new Thread(() -> { /* 任务 */ }).start();。别直接调用 run()——那是同步!

2. 线程安全:共享资源的“守护神”

什么是线程安全?:多线程访问共享数据时,结果与单线程一致(无竞态条件)。问题根源:可见性(缓存不一致)、原子性(操作中断)、有序性(指令重排序)。

JMM(Java 内存模型)原理

  • 主内存:共享变量。
  • 工作内存:线程私有缓存。
  • Happens-Before:保证顺序(如 volatile 写前,读后可见)。

常见问题 & 解决方案表格(实战手册):

问题描述示例解决方案
竞态条件(Race Condition)读-改-写 未原子,如 i++(读 i=1, +1, 写2,但并发读1写2→错)。java<br>int i=0; // 多线程 i++ → i<期望值<br>1. synchronized(互斥锁)。
2. Lock(ReentrantLock,可重入)。
可见性线程A 改变量,B 看不到(缓存)。A: i=1; B: 仍读0。volatile:禁止重排序,强制刷新主内存。
死锁(Deadlock)线程互等锁(如 A 等 B锁,B 等 A锁)。交叉获取锁。1. 固定加锁顺序。
2. tryLock() 超时。
3. jstack 诊断。
指令重排序编译器/JVM 优化,乱序执行。new Object() 后赋引用,先读引用空指针。volatile/double-checked locking。

synchronized 示例(原子 i++):

class Counter {
    private int count = 0;
    public synchronized void increment() { count++; }  // 方法级锁
    // 或代码块:synchronized(this) { count++; }
}

Lock 示例(更灵活,可中断):

import java.util.concurrent.locks.ReentrantLock;
ReentrantLock lock = new ReentrantLock();
lock.lock();
try { /* 关键区 */ } finally { lock.unlock(); }

volatile 示例(标志位):

volatile boolean running = true;
new Thread(() -> {
    while (running) { /* 工作 */ }
}).start();
// 外部:running = false;  // 立即可见

线程安全集合(JUC 救星):

  • ConcurrentHashMap:分段锁,高并发读写。
  • CopyOnWriteArrayList:写时复制,读无锁(适合读多写少)。
  • 示例:ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

小 tip:生产用 Lock > synchronized(性能+功能);诊断用 jvisualvm 看锁争用。

3. 线程池:多线程的“高效管家”

为什么线程池?:new Thread() 贵(创建/销毁开销),池复用线程,控制数量防 OOM。核心:Executor 框架(JUC)。

ThreadPoolExecutor 详解(自定义王道,别用 Executors——易 OOM):

  • 7 参数:corePoolSize(核心数)、maximumPoolSize(最大)、keepAliveTime(空闲存活)、unit、workQueue(队列)、threadFactory、handler(拒绝策略)。
  • 执行流程:任务 < core → 新线程;否则入队;队列满 < max → 新临时线程;全满 → 拒绝。

工厂方法表格(快速上手):

方法参数等价场景代码
newFixedThreadPool(n)core=max=n, 无界队列固定任务,如服务器。java<br>ExecutorService fixed = Executors.newFixedThreadPool(5);<br>
newCachedThreadPool()core=0, max=∞, SynchronousQueue短任务,高吞吐。java<br>ExecutorService cached = Executors.newCachedThreadPool();<br>
newSingleThreadExecutor()core=max=1, 无界队列顺序执行。java<br>ExecutorService single = Executors.newSingleThreadExecutor();<br>

自定义示例(推荐,IO 密集):

import java.util.concurrent.*;
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    4,  // core
    8,  // max
    60L, TimeUnit.SECONDS,  // 空闲回收
    new LinkedBlockingQueue<>(100),  // 队列
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.CallerRunsPolicy()  // 拒绝:调用者执行
);

pool.submit(() -> System.out.println("Task by " + Thread.currentThread().getName()));
pool.shutdown();  // 优雅关闭

拒绝策略

  • AbortPolicy:抛异常(默认)。
  • CallerRunsPolicy:主线程执行(限流)。
  • 示例:队列满时,CallerRuns 防雪崩。

生命周期管理

  • 提交:submit(Callable) 返回 Future(get() 取结果)。
  • 关闭:shutdown() 等任务完;shutdownNow() 中断。
  • 监控:getActiveCount()getQueue().size()

小 tip:CPU 密集 core=CPU核+1;IO 密集 *2。异常处理:Future.get() 捕获 ExecutionException。

4. 完整实战:生产者-消费者(多线程 + 安全 + 池)

场景:队列缓冲,生产者放消息,消费者取(用 BlockingQueue + 线程池)。

import java.util.concurrent.*;

class ProducerConsumer {
    private BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);  // 线程安全队列

    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(5);
        ProducerConsumer pc = new ProducerConsumer();

        // 生产者
        for (int i = 0; i < 3; i++) {
            final int id = i;
            pool.submit(() -> {
                try { pc.queue.put("Msg-" + id); }  // 阻塞放
                catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            });
        }

        // 消费者
        for (int i = 0; i < 2; i++) {
            pool.submit(() -> {
                try {
                    while (true) {
                        String msg = pc.queue.take();  // 阻塞取
                        System.out.println("Consumed: " + msg + " by " + Thread.currentThread().getName());
                    }
                } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            });
        }

        pool.shutdown();
        pool.awaitTermination(10, TimeUnit.SECONDS);
    }
}

输出:看到线程名 + 消息,队列缓冲无竞态。

5. 最佳实践 & 常见陷阱

用表格速查(进阶 checklist):

方面最佳实践陷阱 & 解法
性能线程池复用;volatile + Atomic(CAS,无锁)。过度 synchronized → 性能瓶颈,用 ReadWriteLock 读写分离。
安全避免共享 mutable(如用 immutable 对象);JUC 优先。内存泄漏(未关闭池) → 始终 shutdown + awaitTermination。
调试jstack 线程dump;ThreadMXBean 监控。死锁无迹 → 加 -XX:+PrintGCDetails 查。
进阶ForkJoinPool(递归任务);CompletableFuture(异步链)。忽略中断 → 总是 check Thread.interrupted()。

多线程是 Java 进阶的“核武器”——从入门 Thread 到线程池,你已跨过门槛。实践王道:用 IntelliJ 跑 demo,模拟高并发。下一个?如“JUC 高级锁”或“异步编程”?随时聊!💪(参考:Java 并发编程实战、Oracle 文档)

文章已创建 4972

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部