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)的源码研究,逐步成长为并发编程高手。

相关推荐
爱吃鱼饼的猫9 分钟前
【Spring篇】Spring的生命周期
java·开发语言
Johnny_Cheung13 分钟前
第一次程序Hello Python
开发语言·python
程序猿大波18 分钟前
基于Java,SpringBoot和Vue高考志愿填报辅助系统设计
java·vue.js·spring boot
戴国进21 分钟前
全面讲解python的uiautomation包
开发语言·python
m0_7401546729 分钟前
SpringMVC 请求和响应
java·服务器·前端
橘猫云计算机设计43 分钟前
基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
java·开发语言·数据库·spring boot·微信小程序·小程序·毕业设计
多多*1 小时前
JavaEE企业级开发 延迟双删+版本号机制(乐观锁) 事务保证redis和mysql的数据一致性 示例
java·运维·数据库·redis·mysql·java-ee·wpf
计算机-秋大田1 小时前
基于Spring Boot的个性化商铺系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
叱咤少帅(少帅)1 小时前
Go环境相关理解
linux·开发语言·golang
熬了夜的程序员1 小时前
Go 语言封装邮件发送功能
开发语言·后端·golang·log4j