《Java并发编程从入门到实战:同步、异步、多线程核心原理全解析》
一、多线程基础认知(从单核到多核的进化)
1.1 什么是线程?
线程是程序执行的最小单元,一个进程可以包含多个线程。例如浏览器同时下载文件(后台线程)和渲染页面(UI线程)。
1.2 创建线程的三种方式
java
// 方式1:继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行: " + Thread.currentThread().getName());
}
}
new MyThread().start();
// 方式2:实现Runnable接口(推荐)
Runnable task = () -> {
System.out.println("Runnable线程: " + Thread.currentThread().getName());
};
new Thread(task).start();
// 方式3:使用线程池(生产环境首选)
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
System.out.println("线程池任务: " + Thread.currentThread().getName());
});
executor.shutdown();
1.3 线程生命周期图解
graph TD
A[新建] -->|start()| B[就绪]
B -->|获取CPU| C[运行]
C -->|yield()/时间片用完| B
C -->|sleep()/wait()/IO阻塞| D[阻塞]
D -->|唤醒/IO完成| B
C -->|run()结束| E[终止]
二、同步机制:守护数据安全的金钥匙
2.1 同步问题经典案例
java
class BankAccount {
private int balance = 1000; // 初始余额1000元
// 未同步的取款方法(危险!)
public void withdraw(int amount) {
if (balance >= amount) {
try {
Thread.sleep(100); // 模拟处理延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
balance -= amount;
}
}
public int getBalance() { return balance; }
}
public static void main(String[] args) throws InterruptedException {
BankAccount account = new BankAccount();
// 两个线程同时取款
Thread t1 = new Thread(() -> account.withdraw(800));
Thread t2 = new Thread(() -> account.withdraw(800));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终余额: " + account.getBalance());
// 可能输出-600(实际应为正数)
}
2.2 同步解决方案
java
// 方案1:synchronized方法
public synchronized void withdraw(int amount) { /* 同上 */ }
// 方案2:synchronized代码块
public void withdraw(int amount) {
synchronized(this) {
if (balance >= amount) {
// 业务逻辑
}
}
}
// 方案3:使用ReentrantLock
private Lock lock = new ReentrantLock();
public void withdraw(int amount) {
lock.lock();
try {
if (balance >= amount) {
// 业务逻辑
}
} finally {
lock.unlock(); // 必须手动释放锁
}
}
2.3 同步工具类实战
CountDownLatch(倒计时门闩)
java
// 模拟3个玩家加载完成后开始游戏
CountDownLatch latch = new CountDownLatch(3);
List<Thread> players = Arrays.asList(
new Thread(() -> {
System.out.println("玩家1加载完成");
latch.countDown();
}),
// 其他玩家线程...
);
players.forEach(Thread::start);
latch.await(); // 主线程阻塞等待
System.out.println("所有玩家就绪,游戏开始!");
CyclicBarrier(循环屏障)
java
// 3个线程到达屏障后执行统一动作
CyclicBarrier barrier = new CyclicBarrier(3, () ->
System.out.println("所有线程到达屏障,继续执行"));
IntStream.range(0,3).forEach(i -> new Thread(() -> {
try {
System.out.println("线程"+i+"到达屏障");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start());
三、异步编程:让程序飞起来
3.1 异步回调模式
java
CompletableFuture.supplyAsync(() -> {
// 模拟长时间计算
try { Thread.sleep(2000); } catch (Exception e) {}
return "计算结果";
}).thenAccept(result -> {
System.out.println("获取到结果: " + result);
});
System.out.println("主线程继续执行其他任务...");
3.2 异步任务组合
java
CompletableFuture<String> queryUser = CompletableFuture.supplyAsync(() -> "用户数据");
CompletableFuture<String> queryOrder = CompletableFuture.supplyAsync(() -> "订单数据");
queryUser.thenCombine(queryOrder, (user, order) -> {
return "合并结果: " + user + " + " + order;
}).thenAccept(System.out::println);
3.3 异步异常处理
java
CompletableFuture.supplyAsync(() -> {
if (new Random().nextBoolean()) {
throw new RuntimeException("模拟异常");
}
return "成功结果";
}).exceptionally(ex -> {
System.out.println("捕获异常: " + ex.getMessage());
return "默认值";
}).thenAccept(System.out::println);
四、并发编程实战技巧
4.1 线程池最佳实践
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
60, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 任务队列
new ThreadFactory() { // 自定义线程工厂
private AtomicInteger count = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "业务线程-" + count.getAndIncrement());
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
Future<Integer> future = executor.submit(() -> {
return 1 + 1;
});
// 获取结果
try {
System.out.println(future.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
System.out.println("任务超时");
}
4.2 原子操作类
java
AtomicInteger counter = new AtomicInteger(0);
IntStream.range(0,100).forEach(i -> new Thread(() -> {
counter.incrementAndGet(); // 原子自增
}).start());
System.out.println("最终计数: " + counter.get()); // 保证输出100
4.3 ThreadLocal原理
java
class UserContextHolder {
private static ThreadLocal<User> holder = new ThreadLocal<>();
public static void set(User user) {
holder.set(user);
}
public static User get() {
return holder.get();
}
}
// 在Web请求中设置用户信息
UserContextHolder.set(currentUser);
// 在任何地方获取当前线程用户
User user = UserContextHolder.get();
五、综合应用:电商库存扣减案例
java
class InventoryService {
private AtomicInteger stock = new AtomicInteger(100);
public boolean deductStock(int quantity) {
while (true) {
int current = stock.get();
if (current < quantity) {
return false;
}
if (stock.compareAndSet(current, current - quantity)) {
return true; // CAS成功
}
// CAS失败重试
}
}
}
// 模拟100个并发请求
ExecutorService executor = Executors.newCachedThreadPool();
InventoryService service = new InventoryService();
IntStream.range(0,100).forEach(i -> executor.submit(() -> {
if (service.deductStock(1)) {
System.out.println(Thread.currentThread().getName() + " 扣减成功");
} else {
System.out.println("库存不足");
}
}));
executor.shutdown();
总结与进阶建议
学习路线图:
- 掌握Java内存模型(JMM)与happens-before原则
- 深入理解synchronized锁升级机制(偏向锁->轻量级锁->重量级锁)
- 研究AQS(AbstractQueuedSynchronizer)底层原理
- 学习分布式环境下的并发控制(Redis分布式锁、ZooKeeper选主)
推荐工具:
- VisualVM:监控线程状态与锁竞争
- JMH:编写并发性能测试
- Arthas:在线诊断生产环境并发问题
注意事项:
- 避免过度使用synchronized(可能引发死锁)
- 线程池参数需根据业务特点调整(CPU密集型 vs IO密集型)
- 异步回调中必须处理异常(防止静默失败)
- 使用ThreadLocal后及时remove(防止内存泄漏)
掌握这些知识后,您已经具备处理Java并发编程的基本能力。接下来可以通过《Java并发编程实战》等经典书籍继续深造,同时多参与高并发开源项目(如Netty、RocketMQ)的源码研究,逐步成长为并发编程高手。