
1. 并发编程三大特性解析在分布式系统和高并发场景成为主流的今天理解并发编程的三大核心特性原子性、可见性、有序性已成为开发者必备技能。我曾参与过一个日均请求量超过3000万的电商平台重构深刻体会到对这些概念的模糊认知会导致难以排查的线上事故。本文将从实际案例出发带你穿透理论迷雾。1.1 原子性Atomicity的本质原子性指的是一个操作不可被中断的特性。在Java中看似简单的i操作实际上包含三个步骤读取i的值、对i加1、写回新值。当多个线程同时执行时这三个步骤可能被交替执行// 非原子操作示例 public class Counter { private int count 0; public void increment() { count; // 实际包含read-modify-write三步操作 } }解决原子性问题的主流方案包括synchronizedJVM层面的互斥锁ReentrantLockJDK提供的可重入锁AtomicXXX基于CAS的原子类如AtomicInteger实际经验在秒杀系统开发中AtomicLong的getAndIncrement()方法比synchronized性能高出约40%但在超高并发QPS5万时会出现大量CAS失败此时LongAdder是更好的选择。1.2 可见性Visibility的底层实现可见性问题源于现代CPU的多级缓存架构。当线程A修改了共享变量时线程B可能仍然读取到旧值。以下代码可能永远无法停止public class VisibilityDemo { private static boolean flag true; public static void main(String[] args) throws InterruptedException { new Thread(() - { while (flag) {} // 可能永远循环 System.out.println(Thread stopped); }).start(); Thread.sleep(1000); flag false; } }保证可见性的关键机制volatile关键字强制所有读写直接操作主内存synchronized同步块结束时自动写回主内存final正确初始化的final字段具有可见性踩坑记录在Kafka消费者实现中曾因未对stop标志使用volatile导致消费者线程无法及时停止最终引发消息重复消费。通过JProfiler定位到线程本地缓存未刷新是根本原因。1.3 有序性Ordering与指令重排现代处理器和编译器会进行指令重排序优化导致代码执行顺序与编写顺序不一致。经典的单例模式双重检查锁定问题就源于此public class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance null) { // 第一次检查 synchronized (Singleton.class) { if (instance null) { // 第二次检查 instance new Singleton(); // 可能发生重排序 } } } return instance; } }上述代码可能返回未初始化完成的对象因为new Singleton()包含分配内存空间初始化对象将引用指向内存地址步骤2和3可能被重排序。解决方案包括使用volatile修饰instance使用静态内部类实现推荐方案2. 并发问题实战诊断2.1 原子性问题排查案例在某订单系统中出现库存超卖问题。通过以下步骤定位使用jstack获取线程堆栈发现多个线程同时进入库存扣减逻辑检查发现使用的是普通的int类型计数器// 问题代码 public class Inventory { private int stock 100; public boolean reduceStock() { if (stock 0) { stock--; // 非原子操作 return true; } return false; } }解决方案对比方案QPS(万)平均耗时(ms)适用场景synchronized3.212低并发简单场景ReentrantLock3.89需要公平锁/可中断AtomicInteger4.56计数器等简单操作LongAdder6.13超高并发统计2.2 可见性问题复现与解决开发环境无法复现的线上问题往往与可见性相关。通过以下JMMJava内存模型验证代码可以复现public class JMMDemo { private static /*volatile*/ boolean ready false; private static int number; public static void main(String[] args) { new Thread(() - { while (!ready) { Thread.yield(); } System.out.println(number); // 可能输出0 }).start(); number 42; ready true; } }诊断工具推荐JConsole监控线程状态和内存使用JProfiler分析内存可见性问题Java Mission Control低开销的性能分析2.3 有序性导致的诡异BUG在消息队列消费者实现中曾出现消息处理顺序异常。根本原因是未正确使用happens-before原则public class MessageProcessor { private /*volatile*/ boolean initialized false; private Context context; public void init() { context loadConfig(); // 耗时操作 initialized true; // 可能被重排序到loadConfig前 } public void process(Message msg) { if (initialized) { // 可能看到true但context未初始化 context.handle(msg); } } }解决方案对initialized使用volatile修饰使用final修饰不可变对象使用静态初始化块3. 高并发解决方案进阶3.1 并发工具类实战CountDownLatch在性能测试中的应用public class StressTest { public static void main(String[] args) throws InterruptedException { int threadCount 100; CountDownLatch latch new CountDownLatch(threadCount); long start System.currentTimeMillis(); for (int i 0; i threadCount; i) { new Thread(() - { try { // 模拟业务操作 Thread.sleep(new Random().nextInt(100)); latch.countDown(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } latch.await(); System.out.println(总耗时: (System.currentTimeMillis() - start)); } }ConcurrentHashMap的使用技巧使用computeIfAbsent避免重复计算批量操作使用forEach而不是entrySet遍历统计大Map尺寸时用mappingCount()而非size()3.2 分布式环境下的并发控制当系统扩展到多节点时需要分布式锁解决方案方案对比方案实现方式优点缺点Redis锁SETNX Lua脚本性能高非强一致Zookeeper临时顺序节点可靠性高性能较低数据库唯一索引/乐观锁实现简单并发能力差Redis分布式锁最佳实践public class RedisLock { private static final String LOCK_SUCCESS OK; private static final Long RELEASE_SUCCESS 1L; public static boolean tryLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result jedis.set(lockKey, requestId, NX, PX, expireTime); return LOCK_SUCCESS.equals(result); } public static boolean releaseLock(Jedis jedis, String lockKey, String requestId) { String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; Object result jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); return RELEASE_SUCCESS.equals(result); } }3.3 并发性能优化技巧锁细化将大锁拆分为多个小锁错误示例对整个用户服务加锁正确做法按用户ID哈希分片加锁无锁编程使用并发容器和原子类// 使用ConcurrentHashMap替代同步的HashMap MapString, Integer map new ConcurrentHashMap(); // 使用AtomicStampedReference解决ABA问题 AtomicStampedReferenceInteger atomicRef new AtomicStampedReference(0, 0);线程池调优ThreadPoolExecutor executor new ThreadPoolExecutor( corePoolSize, // CPU核心数*2 maximumPoolSize, // 核心数*4 60, TimeUnit.SECONDS, new LinkedBlockingQueue(1000), // 根据内存设置 new ThreadFactoryBuilder().setNameFormat(worker-%d).build(), new ThreadPoolExecutor.CallerRunsPolicy() // 降级策略 );4. 常见问题排查手册4.1 死锁诊断与预防诊断步骤jstack pid获取线程dump查找deadlock关键词分析相互等待的线程链预防措施使用tryLock设置超时时间统一锁的获取顺序使用jconsole监控锁竞争4.2 线程泄漏排查典型症状线程数持续增长应用响应变慢最终OOM排查工具# 查看线程数变化 watch -n 1 jstack pid | grep java.lang.Thread.State | wc -l # 统计各种状态的线程 jstack pid | awk {print $2} | sort | uniq -c4.3 性能瓶颈定位锁竞争使用jstack查看阻塞线程通过arthas的monitor命令统计方法耗时上下文切换# 查看上下文切换次数 vmstat 1 pidstat -w -p pid 1CPU热点使用async-profiler生成火焰图JMC的飞行记录器分析5. 新一代并发编程模型5.1 协程与虚拟线程Java19引入的虚拟线程协程使用示例try (var executor Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i - { executor.submit(() - { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); } // 自动等待所有任务完成与传统线程池对比指标平台线程虚拟线程创建成本1MB/线程几百字节切换成本内核态切换用户态切换最大数量数千数百万5.2 响应式编程Spring WebFlux示例public interface UserRepository extends ReactiveCrudRepositoryUser, Long { FluxUser findByAgeGreaterThan(int age); } RestController public class UserController { GetMapping(/users) public FluxUser listUsers() { return userRepository.findAll() .delayElements(Duration.ofMillis(100)) .log(); } }背压处理策略BUFFER缓冲处理可能OOMDROP丢弃溢出元素LATEST只保留最新元素ERROR直接报错5.3 无锁数据结构进阶比较并交换(CAS)的底层实现public final class AtomicInteger extends Number { private static final Unsafe unsafe Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField(value)); } catch (Exception ex) { throw new Error(ex); } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } }适用场景计数器、累加器状态标志位更新简单对象的原子引用更新在开发实践中我逐渐形成了这样的认知理解并发三特性只是起点真正的挑战在于根据具体业务场景选择合适的并发模型。比如在最近开发的实时风控系统中结合了虚拟线程处理IO密集型任务和传统线程池处理CPU密集型任务相比纯异步回调方式降低了50%的代码复杂度。