一、不可变对象模式

1.1 不可变对象的设计

/**
 * 标准的不可变对象设计
 */
public final class ImmutableUser {
    private final String name;
    private final int age;
    private final List<String> hobbies;

    public ImmutableUser(String name, int age, List<String> hobbies) {
        this.name = name;
        this.age = age;
        // 防御性复制
        this.hobbies = new ArrayList<>(hobbies);
    }

    public String getName() { return name; }
    public int getAge() { return age; }

    public List<String> getHobbies() {
        // 返回不可变视图
        return Collections.unmodifiableList(hobbies);
    }
}

/*
不可变对象的五大要求:
1. ✅ 类声明为final
2. ✅ 所有字段都是final
3. ✅ 所有字段都是private
4. ✅ 不提供setter方法
5. ✅ 可变字段防御性复制

优势:
├─ 天然线程安全
├─ 可以安全共享
└─ 适合做缓存key

适用场景:String、Integer、LocalDate等
*/

二、ThreadLocal模式

2.1 基本使用

/**
 * ThreadLocal:线程本地存储
 */
public class ThreadLocalExample {
    // 每个线程独立的SimpleDateFormat
    private static ThreadLocal<SimpleDateFormat> dateFormat =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

    public static String formatDate(Date date) {
        return dateFormat.get().format(date);  // 线程安全
    }

    public static void main(String[] args) {
        // 10个线程并发格式化日期
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                System.out.println(formatDate(new Date()));
            }).start();
        }
    }
}

/*
使用场景:
1. SimpleDateFormat(线程不安全)
2. 数据库连接(每个线程独立连接)
3. 用户上下文(Spring Security)
*/

2.2 内存泄漏问题

/**
 * ThreadLocal内存泄漏问题
 */
public class ThreadLocalMemoryLeak {
    private static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        // 线程池场景
        ExecutorService executor = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                // 存储大对象
                threadLocal.set(new byte[1024 * 1024]);  // 1MB

                // 业务逻辑
                // ...

                // ❌ 忘记清理,导致内存泄漏
                // threadLocal.remove();  // 正确做法
            });
        }

        executor.shutdown();
    }

    /*
    内存泄漏原因:
    1. ThreadLocal存储在ThreadLocalMap中
    2. key是弱引用(ThreadLocal)
    3. value是强引用(实际对象)
    4. 线程池线程复用,ThreadLocalMap一直存在
    5. value无法被GC,导致内存泄漏

    解决方案:
    try {
        threadLocal.set(value);
        // 业务逻辑
    } finally {
        threadLocal.remove();  // 必须清理
    }
    */
}

三、两阶段终止模式

3.1 优雅停止线程

/**
 * 正确的线程停止方式
 */
public class GracefulShutdown {
    private volatile boolean running = true;

    public void start() {
        Thread thread = new Thread(() -> {
            while (running) {  // 检查标志位
                try {
                    // 业务逻辑
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // 响应中断
                    Thread.currentThread().interrupt();
                    break;
                }
            }
            // 清理资源
            cleanup();
        });
        thread.start();
    }

    public void stop() {
        running = false;  // 第一阶段:设置标志位
    }

    private void cleanup() {
        // 第二阶段:清理资源
        System.out.println("清理资源");
    }

    /*
    错误做法:
    thread.stop();  // ❌ 已废弃,不安全

    正确做法:
    1. volatile标志位
    2. interrupt()中断
    3. 两阶段:设置标志 + 清理资源
    */
}

四、读写锁模式

4.1 ReentrantReadWriteLock

/**
 * 读写锁:读多写少场景
 */
public class ReadWriteLockExample {
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Map<String, String> cache = new HashMap<>();

    // 读操作:共享锁
    public String get(String key) {
        rwLock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    // 写操作:排他锁
    public void put(String key, String value) {
        rwLock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    /*
    读写锁特性:
    ├─ 读-读:可并发
    ├─ 读-写:互斥
    └─ 写-写:互斥

    性能提升:
    读多写少场景,性能比synchronized高3-5倍
    */
}

4.2 StampedLock(JDK 8)

/**
 * StampedLock:性能更好的读写锁
 */
public class StampedLockExample {
    private final StampedLock lock = new StampedLock();
    private double x, y;

    // 乐观读
    public double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();  // 乐观读
        double currentX = x;
        double currentY = y;

        if (!lock.validate(stamp)) {  // 验证是否被修改
            stamp = lock.readLock();  // 升级为悲观读锁
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    // 写锁
    public void move(double deltaX, double deltaY) {
        long stamp = lock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    /*
    StampedLock优势:
    1. 乐观读不加锁(性能极高)
    2. 读写性能比ReentrantReadWriteLock高
    3. 适合读多写少场景

    注意:
    ❌ 不支持重入
    ❌ 不支持Condition
    */
}

五、并发编程最佳实践

5.1 核心原则

1. 优先使用不可变对象
   └─ String、Integer、LocalDate

2. 减少锁的范围
   └─ 只锁必要的代码

3. 避免锁嵌套
   └─ 容易死锁

4. 使用并发工具类
   ├─ AtomicInteger替代synchronized计数
   ├─ ConcurrentHashMap替代Hashtable
   ├─ CountDownLatch/CyclicBarrier协调线程
   └─ BlockingQueue实现生产者-消费者

5. 线程池管理
   ├─ 自定义ThreadPoolExecutor(禁用Executors)
   ├─ 优雅关闭(shutdown + awaitTermination)
   └─ 线程命名(便于排查)

6. 异常处理
   ├─ 捕获InterruptedException
   ├─ 设置UncaughtExceptionHandler
   └─ 避免异常导致线程死亡

5.2 常见陷阱

/**
 * 常见并发陷阱
 */
public class ConcurrencyTraps {
    // ❌ 陷阱1:双重检查锁定错误
    private static Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();  // 需要volatile
                }
            }
        }
        return instance;
    }
    // ✅ 正确:private static volatile Singleton instance;

    // ❌ 陷阱2:SimpleDateFormat共享
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    // ✅ 正确:使用ThreadLocal或DateTimeFormatter

    // ❌ 陷阱3:复合操作不原子
    if (!map.containsKey(key)) {
        map.put(key, value);
    }
    // ✅ 正确:map.putIfAbsent(key, value);

    // ❌ 陷阱4:忘记释放锁
    lock.lock();
    // 业务逻辑
    lock.unlock();  // 异常时不会执行
    // ✅ 正确:try-finally

    // ❌ 陷阱5:使用Executors工厂方法
    Executors.newFixedThreadPool(10);  // 无界队列
    // ✅ 正确:自定义ThreadPoolExecutor

    static class Singleton {
        private static volatile Singleton instance;
        public static Singleton getInstance() { return instance; }
    }
}

5.3 性能调优

1. 减少锁竞争
   ├─ 缩小锁范围
   ├─ 降低锁粒度(分段锁)
   └─ 使用无锁算法(CAS)

2. 减少上下文切换
   ├─ 控制线程数(CPU核心数 × 2)
   ├─ 使用协程/虚拟线程(JDK 19+)
   └─ 避免频繁阻塞

3. 内存优化
   ├─ 对象池(避免频繁创建)
   ├─ ThreadLocal注意清理
   └─ 合理设置JVM参数

4. 监控与调优
   ├─ JProfiler监控线程状态
   ├─ Arthas诊断死锁
   └─ 压测验证性能

六、总结:并发编程知识体系

6.1 核心知识点回顾

第1篇:并发三大问题
├─ 可见性:CPU缓存导致
├─ 原子性:指令交错执行
├─ 有序性:指令重排序
└─ JMM:happens-before规则

第2篇:同步机制
├─ synchronized:锁升级(偏向锁→轻量级锁→重量级锁)
├─ Lock:ReentrantLock、公平锁/非公平锁
├─ AQS:CLH队列、独占/共享模式
└─ 死锁:四个必要条件、预防策略

第3篇:无锁编程
├─ CAS:CPU的CMPXCHG指令
├─ Atomic:AtomicInteger、LongAdder
├─ 并发工具:CountDownLatch、Semaphore
└─ 无锁数据结构:无锁栈、无锁队列

第4篇:线程池与异步
├─ ThreadPoolExecutor:七参数、四拒绝策略
├─ ForkJoinPool:工作窃取算法
├─ CompletableFuture:组合式异步编程
└─ 最佳实践:配置、监控、优雅关闭

第5篇:并发集合
├─ ConcurrentHashMap:分段锁→CAS+synchronized
├─ BlockingQueue:生产者-消费者模式
├─ CopyOnWriteArrayList:写时复制
└─ 选择指南:性能对比、适用场景

第6篇:设计模式与实践
├─ 不可变对象:天然线程安全
├─ ThreadLocal:线程本地存储
├─ 读写锁:读多写少优化
└─ 最佳实践:避坑指南、性能调优

6.2 学习路径建议

阶段1:理解原理(1-2周)
├─ JMM、happens-before
├─ 三大问题
└─ volatile、final

阶段2:掌握同步(2-3周)
├─ synchronized原理
├─ Lock接口
└─ AQS源码

阶段3:无锁编程(2-3周)
├─ CAS原理
├─ Atomic类
└─ 并发工具类

阶段4:工程实践(4-6周)
├─ 线程池配置
├─ 并发集合使用
├─ 实际项目应用
└─ 性能调优

总计:约3个月系统学习

6.3 推荐资源

经典书籍:
1. 《Java并发编程实战》 ⭐⭐⭐⭐⭐
2. 《Java并发编程的艺术》 ⭐⭐⭐⭐
3. 《深入理解Java虚拟机》 ⭐⭐⭐⭐⭐

官方文档:
1. JDK源码(java.util.concurrent包)
2. Java Language Specification - Chapter 17
3. JSR-133: Java Memory Model

开源项目:
1. Netty(高性能网络框架)
2. Disruptor(高性能队列)
3. Spring(DI容器、事务管理)

结语

Java并发编程系列完结

从并发三大问题到设计模式最佳实践,我们完整地探索了Java并发编程的知识体系:

  • 第一性原理:从"为什么"出发,理解本质
  • 渐进式学习:从原理到实践,循序渐进
  • 真实案例:秒杀、缓存、线程池,贴近实战
  • 性能数据:量化收益,理性决策

核心收获

  1. 理论基础:JMM、happens-before、三大问题
  2. 同步机制:synchronized、Lock、AQS
  3. 无锁编程:CAS、Atomic、并发工具类
  4. 资源管理:线程池、CompletableFuture
  5. 并发集合:ConcurrentHashMap、BlockingQueue
  6. 最佳实践:设计模式、避坑指南

下一步建议

  • ✅ 阅读JDK并发包源码
  • ✅ 实际项目中应用
  • ✅ 性能压测与调优
  • ✅ 关注JDK新特性(虚拟线程等)

并发编程是一门艺术,需要理论与实践相结合。希望这个系列能帮助你建立完整的并发编程知识体系!


系列文章(全部完结):

  1. ✅ Java并发第一性原理:为什么并发如此困难?
  2. ✅ 线程安全与同步机制:从synchronized到Lock的演进
  3. ✅ 并发工具类与原子操作:无锁编程的艺术
  4. ✅ 线程池与异步编程:从Thread到CompletableFuture的演进
  5. ✅ 并发集合:从HashMap到ConcurrentHashMap的演进
  6. 并发设计模式与最佳实践(本文)

系列统计

  • 总字数:约90,000字
  • 文章数:6篇
  • 代码示例:100+个
  • 涵盖主题:从JMM到最佳实践的完整体系

感谢阅读!如果这个系列对你有帮助,欢迎分享给更多的Java开发者!