马士兵 【Java多线程与高并发】从入门到精髓

197 阅读4分钟

Java多线程与高并发:从基础到高阶实战全解析(2024最新版)

摘要:在互联网高并发场景下,Java多线程技术是提升系统性能的核心利器。本文系统讲解多线程基础、线程安全、锁优化、并发容器等核心知识,并深度剖析高并发场景下的性能调优技巧,结合电商秒杀案例,助你彻底掌握Java并发编程精髓。(关键词:Java多线程、高并发、线程安全、锁机制、性能优化)

一、多线程基础:从进程到线程池的演进

1. 核心概念解析

术语定义关联场景
进程操作系统资源分配的基本单位独立运行的应用程序(如Chrome浏览器)
线程CPU调度的最小单位,共享进程内存空间文件下载的多任务处理
并发多个任务在单核上交替执行用户请求的快速响应
并行多核CPU同时处理多个任务大数据计算(MapReduce)

代码示例:创建线程的三种方式

java

复制

// 方式1:继承Thread类  
class MyThread extends Thread {  
    public void run() {  
        System.out.println("Thread running");  
    }  
}  

// 方式2:实现Runnable接口  
Runnable task = () -> System.out.println("Runnable running");  
new Thread(task).start();  

// 方式3:线程池(推荐)  
ExecutorService executor = Executors.newFixedThreadPool(4);  
executor.submit(() -> System.out.println("Pool thread running"));  

2. 线程生命周期与状态转换

Java线程状态图

  • 新建(NEW) :线程对象创建但未启动
  • 就绪(RUNNABLE) :调用start()后等待CPU调度
  • 运行(RUNNING) :正在执行run()方法
  • 阻塞(BLOCKED) :等待锁或I/O操作
  • 终止(TERMINATED) :run()执行结束

二、线程安全与锁机制:高并发的基石

1. 线程安全三大问题

  • 原子性破坏:i++非原子操作导致计数错误
  • 可见性问题:CPU缓存导致数据不同步
  • 有序性破坏:指令重排序引发逻辑错误

案例:多线程卖票问题

java

复制

public class TicketSeller {  
    private int tickets = 100;  

    public void sell() {  
        if (tickets > 0) {  
            System.out.println(Thread.currentThread().getName() + "卖出第" + tickets-- + "张票");  
        }  
    }  
}  
// 多个线程同时调用sell()会导致超卖  

2. 解决方案对比

方案实现方式适用场景性能影响
synchronized关键字修饰方法或代码块简单同步需求较高
ReentrantLock显式锁API,支持公平锁/条件变量复杂锁控制中等
CASAtomic类基于CPU指令实现计数器等高频轻量级操作

代码示例:使用ReentrantLock解决卖票问题

java

复制

private final ReentrantLock lock = new ReentrantLock();  

public void sell() {  
    lock.lock();  
    try {  
        if (tickets > 0) {  
            System.out.println(Thread.currentThread().getName() + "卖出第" + tickets-- + "张票");  
        }  
    } finally {  
        lock.unlock();  
    }  
}  

三、高并发实战:电商秒杀系统优化案例

1. 需求分析

  • 峰值QPS:10万+/秒
  • 核心挑战:库存扣减的原子性、防止超卖、系统抗压能力

2. 技术方案设计

复制

┌───────────────┐     ┌──────────────┐  
│   客户端请求     │ →  │   Nginx负载均衡 │  
└───────────────┘     └──────────────┘  
                             │  
                ┌────────────┼────────────┐  
                ▼           ▼           ▼  
      ┌─────────────┐ ┌─────────────┐ ┌─────────────┐  
      │ Redis集群    │ │ 消息队列      │ │ 数据库分库分表 │  
      │ (库存预扣减)  │ │ (异步下单)    │ │ (最终一致性)  │  
      └─────────────┘ └─────────────┘ └─────────────┘  

3. 关键代码实现

Redis+Lua保证原子扣减

lua

复制

local key = KEYS[1]  
local num = tonumber(ARGV[1])  
local stock = tonumber(redis.call('get', key))  
if stock >= num then  
    redis.call('decrby', key, num)  
    return 1 -- 成功  
else  
    return 0 -- 库存不足  
end  

Java调用示例

java

复制

public boolean deductStock(String productId, int num) {  
    String script = "lua脚本内容";  
    Long result = jedis.eval(script, Collections.singletonList(productId), Collections.singletonList(String.valueOf(num)));  
    return result == 1;  
}  

四、性能调优:从锁优化到并发容器

1. 锁粒度优化技巧

  • 细分锁:将大锁拆分为多个小锁(如ConcurrentHashMap的分段锁)
  • 读写分离:使用ReentrantReadWriteLock提升读多写少场景性能
  • 无锁化:基于ThreadLocal实现线程本地计数

2. 并发容器选型指南

容器线程安全原理适用场景
ConcurrentHashMap分段锁+CAS高频读写的键值存储
CopyOnWriteArrayList写时复制读多写少的列表操作
BlockingQueue锁+条件变量生产者-消费者模型

代码示例:ConcurrentHashMap使用

java

复制

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();  
map.compute("key", (k, v) -> v == null ? 1 : v + 1);  

五、避坑指南:多线程开发中的5大陷阱

1. 死锁的产生与排查

示例代码

java

复制

// 线程1  
synchronized (lockA) {  
    synchronized (lockB) { /* ... */ }  
}  

// 线程2  
synchronized (lockB) {  
    synchronized (lockA) { /* ... */ }  
}  

排查工具

  • jstack PID 查看线程堆栈
  • VisualVM检测死锁

2. 线程池参数配置误区

  • 错误做法:盲目使用Executors.newFixedThreadPool导致OOM
  • 正确方案:自定义ThreadPoolExecutor并指定拒绝策略

java

复制

new ThreadPoolExecutor(  
    4, // 核心线程数  
    8, // 最大线程数  
    60, TimeUnit.SECONDS,  
    new LinkedBlockingQueue<>(1000),  
    new ThreadPoolExecutor.AbortPolicy()  
);  

六、未来趋势:虚拟线程(Loom项目)初探

Java 19引入的虚拟线程(Virtual Threads)将颠覆传统并发模型:

java

复制

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {  
    IntStream.range(0, 10_000).forEach(i -> {  
        executor.submit(() -> {  
            Thread.sleep(Duration.ofSeconds(1));  
            return i;  
        });  
    });  
}  

优势

  • 轻量级:可创建数百万个虚拟线程
  • 兼容性:与现有Thread API无缝集成

结语:成为高并发专家的学习路径

  1. 夯实基础:精读《Java并发编程实战》
  2. 实战演练:参与开源项目(如Apache Dubbo)
  3. 性能调优:使用Arthas、JMeter进行压测分析

立即行动:评论区回复“并发资料”,免费获取《Java高并发核心知识图谱》!