Java并发编程从入门到实战:同步、异步、多线程核心原理全解析

《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();

总结与进阶建议

学习路线图

  1. 掌握Java内存模型(JMM)与happens-before原则
  2. 深入理解synchronized锁升级机制(偏向锁->轻量级锁->重量级锁)
  3. 研究AQS(AbstractQueuedSynchronizer)底层原理
  4. 学习分布式环境下的并发控制(Redis分布式锁、ZooKeeper选主)

推荐工具

  • VisualVM:监控线程状态与锁竞争
  • JMH:编写并发性能测试
  • Arthas:在线诊断生产环境并发问题

注意事项

  1. 避免过度使用synchronized(可能引发死锁)
  2. 线程池参数需根据业务特点调整(CPU密集型 vs IO密集型)
  3. 异步回调中必须处理异常(防止静默失败)
  4. 使用ThreadLocal后及时remove(防止内存泄漏)

掌握这些知识后,您已经具备处理Java并发编程的基本能力。接下来可以通过《Java并发编程实战》等经典书籍继续深造,同时多参与高并发开源项目(如Netty、RocketMQ)的源码研究,逐步成长为并发编程高手。

相关推荐
Scott9999HH4 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社5 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海5 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖6 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区8 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai9 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说9 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json