并发设计模式:经典模式与最佳实践

一、生产者-消费者模式 1.1 核心思想 生产者线程 → [队列] → 消费者线程 - 生产者:生产数据,放入队列 - 消费者:从队列取出数据,处理 - 队列:解耦生产者和消费者 优势: ✅ 解耦:生产者和消费者独立 ✅ 削峰填谷:队列缓冲,应对突发流量 ✅ 异步处理:提高响应速度 1.2 BlockingQueue实现 public class ProducerConsumerDemo { private BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100); // 生产者 class Producer implements Runnable { @Override public void run() { while (true) { try { Task task = produceTask(); queue.put(task); // 队列满时阻塞 System.out.println("生产:" + task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } private Task produceTask() { // 生产任务 return new Task(); } } // 消费者 class Consumer implements Runnable { @Override public void run() { while (true) { try { Task task = queue.take(); // 队列空时阻塞 processTask(task); System.out.println("消费:" + task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } private void processTask(Task task) { // 处理任务 } } public static void main(String[] args) { ProducerConsumerDemo demo = new ProducerConsumerDemo(); // 启动3个生产者 for (int i = 0; i < 3; i++) { new Thread(demo.new Producer()).start(); } // 启动5个消费者 for (int i = 0; i < 5; i++) { new Thread(demo.new Consumer()).start(); } } } 二、线程池模式 2.1 核心思想 任务提交 → [线程池] → 线程执行 - 预创建线程,复用线程资源 - 避免频繁创建/销毁线程的开销 - 控制并发数量,避免资源耗尽 2.2 自定义线程池 public class CustomThreadPool { private final BlockingQueue<Runnable> taskQueue; private final List<WorkerThread> workers; private volatile boolean isShutdown = false; public CustomThreadPool(int poolSize, int queueSize) { taskQueue = new LinkedBlockingQueue<>(queueSize); workers = new ArrayList<>(poolSize); // 创建工作线程 for (int i = 0; i < poolSize; i++) { WorkerThread worker = new WorkerThread(); workers.add(worker); worker.start(); } } // 提交任务 public void submit(Runnable task) { if (isShutdown) { throw new IllegalStateException("线程池已关闭"); } try { taskQueue.put(task); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } // 关闭线程池 public void shutdown() { isShutdown = true; for (WorkerThread worker : workers) { worker.interrupt(); } } // 工作线程 private class WorkerThread extends Thread { @Override public void run() { while (!isShutdown) { try { Runnable task = taskQueue.take(); task.run(); } catch (InterruptedException e) { break; } } } } } 三、Future模式 3.1 核心思想 主线程提交任务 → 异步执行 → 主线程继续工作 → 需要时获取结果 - 异步获取结果 - 主线程不阻塞 - 提高并发性能 3.2 简化实现 public class FutureDemo { // 自定义Future class FutureResult<T> { private volatile T result; private volatile boolean isDone = false; public void set(T result) { this.result = result; this.isDone = true; synchronized (this) { notifyAll(); // 唤醒等待线程 } } public T get() throws InterruptedException { if (!isDone) { synchronized (this) { while (!isDone) { wait(); // 等待结果 } } } return result; } } // 异步任务 public FutureResult<String> asyncTask() { FutureResult<String> future = new FutureResult<>(); new Thread(() -> { // 模拟耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // 设置结果 future.set("异步任务完成"); }).start(); return future; // 立即返回Future } public static void main(String[] args) throws InterruptedException { FutureDemo demo = new FutureDemo(); // 提交异步任务 FutureResult<String> future = demo.asyncTask(); // 主线程继续工作 System.out.println("主线程继续工作..."); // 需要时获取结果 String result = future.get(); System.out.println("结果:" + result); } } 四、不变模式(Immutable Pattern) 4.1 核心思想 对象创建后,状态不可改变 - 无需同步:线程安全 - 简化并发:无竞态条件 - 高性能:无锁开销 4.2 实现方式 // 不变对象 public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } // 修改操作返回新对象 public ImmutablePoint move(int deltaX, int deltaY) { return new ImmutablePoint(x + deltaX, y + deltaY); } } // 使用 ImmutablePoint p1 = new ImmutablePoint(0, 0); ImmutablePoint p2 = p1.move(10, 10); // 新对象 // 多线程安全:p1和p2都不会被修改 关键要素: ...

2025-11-20 · maneng

消息中间件核心概念:从生产消费到发布订阅

为什么需要Producer和Consumer?为什么需要Topic和Queue?什么是"至少一次"和"恰好一次"? 本文从零开始,通过手写代码和渐进式演进,深度拆解消息中间件的核心概念。 一、消息模型演进:从点对点到发布订阅 1.1 Level 0:最简模型(内存队列) 场景:单机应用,生产者和消费者在同一个进程。 /** * 最简单的消息队列:内存队列 * 使用Java BlockingQueue实现 */ public class SimpleMessageQueue { // 内存队列(线程安全) private final BlockingQueue<Message> queue = new LinkedBlockingQueue<>(1000); /** * 生产者:发送消息 */ public void send(Message message) throws InterruptedException { queue.put(message); System.out.println("发送消息: " + message); } /** * 消费者:接收消息 */ public Message receive() throws InterruptedException { Message message = queue.take(); System.out.println("接收消息: " + message); return message; } /** * 消息实体 */ @Data @AllArgsConstructor public static class Message { private String id; private String content; private long timestamp; } /** * 测试代码 */ public static void main(String[] args) { SimpleMessageQueue mq = new SimpleMessageQueue(); // 生产者线程 Thread producer = new Thread(() -> { for (int i = 0; i < 10; i++) { try { Message msg = new Message( "MSG-" + i, "Hello " + i, System.currentTimeMillis() ); mq.send(msg); Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); // 消费者线程 Thread consumer = new Thread(() -> { for (int i = 0; i < 10; i++) { try { mq.receive(); Thread.sleep(200); // 消费速度慢于生产速度 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); producer.start(); consumer.start(); } } 输出示例: ...

2025-11-03 · maneng

Spring源码深度解析:从使用者到贡献者

系列导航:本文是《Spring框架第一性原理》系列的第6篇(最终篇) 第1篇:为什么我们需要Spring框架? 第2篇:IoC容器:从手动new到自动装配的演进 第3篇:AOP:从代码重复到面向切面编程 第4篇:Spring Boot:约定优于配置的威力 第5篇:Spring Cloud:从单体到微服务的架构演进 第6篇:Spring源码深度解析(本文) 引子:为什么要阅读Spring源码? 三个真实场景 场景1:面试被问倒 面试官:"请说说Spring容器的启动流程?" 候选人:"Spring容器启动时会扫描注解,创建Bean,然后注入依赖..." 面试官:"具体一点,refresh()方法做了哪些事情?" 候选人:"呃...不太清楚..." 面试官:"那@Transactional是如何实现的?" 候选人:"通过AOP代理..." 面试官:"什么时候创建代理?代理怎么拦截方法?" 候选人:"这个...没深入研究过..." 结果:错失Offer 场景2:生产问题无从下手 线上问题:Spring容器启动失败 异常信息: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderService': Unsatisfied dependency expressed through field 'userService' 问题: ├─ orderService依赖userService ├─ 但容器中没有userService的Bean ├─ 为什么没有创建? └─ 如何定位问题? 不懂源码: ❌ 只能Google搜索错误信息 ❌ 盲目尝试各种配置 ❌ 浪费大量时间 懂源码: ✅ 知道Bean的创建流程 ✅ 知道依赖注入的机制 ✅ 快速定位到扫描路径配置错误 场景3:性能优化无从优化 问题:Spring应用启动慢(需要30秒) 不懂源码: ├─ 不知道启动时间花在哪里 ├─ 盲目减少Bean数量 └─ 效果有限 懂源码: ├─ 知道refresh()方法的12个步骤 ├─ 知道Bean创建的生命周期 ├─ 定位到瓶颈:@ComponentScan扫描了大量不必要的类 ├─ 优化扫描范围 └─ 启动时间降到5秒 阅读源码的五大收益 1. 面试加分 └─ 深入理解Spring原理,回答问题有理有据 └─ 知其然也知其所以然 2. 问题定位 └─ 遇到问题时,能快速定位根因 └─ 不再盲目Google,节省时间 3. 性能优化 └─ 知道性能瓶颈在哪里 └─ 有针对性地优化 4. 技术提升 └─ 学习优秀的代码设计 └─ 学习设计模式的实战应用 5. 贡献开源 └─ 有能力向Spring提交PR └─ 提升技术影响力 源码阅读准备 开发环境搭建 1. 下载Spring源码 # 1. 克隆Spring Framework仓库 git clone https://github.com/spring-projects/spring-framework.git # 2. 切换到稳定版本(推荐5.3.x) cd spring-framework git checkout v5.3.25 # 3. 查看项目结构 tree -L 1 . ├── spring-aop # AOP模块 ├── spring-aspects # AspectJ集成 ├── spring-beans # Bean管理核心 ├── spring-context # 应用上下文 ├── spring-core # 核心工具类 ├── spring-expression # SpEL表达式 ├── spring-jdbc # JDBC支持 ├── spring-orm # ORM集成 ├── spring-tx # 事务管理 ├── spring-web # Web基础 ├── spring-webmvc # Spring MVC └── spring-webflux # 响应式Web 2. 导入IDEA 步骤: 1. 打开IDEA → Open → 选择spring-framework目录 2. 等待Gradle构建完成(首次需要10-30分钟) 3. 构建完成后,项目结构如下: spring-framework/ ├── spring-beans/ │ └── src/main/java/ │ └── org/springframework/beans/ │ ├── factory/ │ │ ├── BeanFactory.java # Bean工厂接口 │ │ ├── support/ │ │ │ ├── AbstractBeanFactory.java │ │ │ └── DefaultListableBeanFactory.java │ │ └── config/ │ │ └── BeanDefinition.java # Bean定义 │ └── BeanWrapper.java ├── spring-context/ │ └── src/main/java/ │ └── org/springframework/context/ │ ├── ApplicationContext.java # 应用上下文接口 │ ├── support/ │ │ └── AbstractApplicationContext.java # 启动核心 │ └── annotation/ │ └── AnnotationConfigApplicationContext.java └── spring-aop/ └── src/main/java/ └── org/springframework/aop/ └── framework/ ├── ProxyFactory.java └── autoproxy/ └── AbstractAutoProxyCreator.java 常用快捷键: ├─ Ctrl+N (Mac: Cmd+O):搜索类 ├─ Ctrl+Shift+N:搜索文件 ├─ Ctrl+Alt+B (Mac: Cmd+Alt+B):查看接口实现 ├─ Ctrl+H:查看类层次结构 └─ Ctrl+F12:查看类的方法列表 3. 编写测试用例 // 创建测试项目:spring-source-test // pom.xml <dependencies> <!-- Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.25</version> </dependency> <!-- 测试依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> // 测试代码 @Configuration @ComponentScan("com.example") public class AppConfig { } @Component public class UserService { public void sayHello() { System.out.println("Hello from UserService"); } } // 测试类 public class SpringSourceTest { public static void main(String[] args) { // 在这里打断点,开始调试 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); userService.sayHello(); } } 调试技巧: 1. 在new AnnotationConfigApplicationContext(AppConfig.class)打断点 2. Step Into (F7),进入构造方法 3. 一步步跟踪执行流程 4. 关注关键方法:refresh()、invokeBeanFactoryPostProcessors()、finishBeanFactoryInitialization() 源码阅读方法论 方法1:从顶层接口入手 阅读顺序(IoC容器): 1. BeanFactory # 最顶层接口,定义基本功能 ├─ getBean() # 获取Bean ├─ containsBean() # 是否包含Bean └─ isSingleton() # 是否单例 2. ApplicationContext # 扩展接口,增强功能 ├─ 继承BeanFactory # Bean管理 ├─ 继承MessageSource # 国际化 ├─ 继承ApplicationEventPublisher # 事件发布 └─ 继承ResourceLoader # 资源加载 3. AbstractApplicationContext # 抽象实现类,定义模板 └─ refresh() # 核心方法,定义启动流程 4. AnnotationConfigApplicationContext # 具体实现类 └─ 使用注解配置 原则: ✅ 先看接口,理解设计意图 ✅ 再看抽象类,理解模板流程 ✅ 最后看具体实现,理解细节 ❌ 避免:直接看具体实现(容易迷失) 方法2:以功能为主线 功能主线1:Bean的创建流程 └─ 入口:ApplicationContext.getBean() └─ AbstractBeanFactory.doGetBean() └─ AbstractAutowireCapableBeanFactory.createBean() └─ AbstractAutowireCapableBeanFactory.doCreateBean() ├─ createBeanInstance() # 实例化 ├─ populateBean() # 属性填充 └─ initializeBean() # 初始化 功能主线2:AOP代理的创建 └─ 入口:@EnableAspectJAutoProxy └─ AspectJAutoProxyRegistrar └─ AnnotationAwareAspectJAutoProxyCreator └─ postProcessAfterInitialization() # Bean后置处理 └─ wrapIfNecessary() # 创建代理 功能主线3:事务的拦截 └─ 入口:@EnableTransactionManagement └─ TransactionInterceptor └─ invoke() # 拦截方法 ├─ createTransactionIfNecessary() # 开启事务 ├─ invokation.proceed() # 执行方法 └─ commitTransactionAfterReturning() # 提交事务 原则: ✅ 选择一个功能点,深入研究 ✅ 画出调用链路图 ✅ 理解每个方法的职责 ❌ 避免:同时看多个功能(容易混乱) 方法3:Debug + 日志 // 开启Spring Debug日志 logging.level.org.springframework=DEBUG // 在关键位置打断点 public class SpringSourceTest { public static void main(String[] args) { // 断点1:容器创建 ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 断点2:Bean获取 UserService userService = context.getBean(UserService.class); // 断点3:方法调用 userService.sayHello(); } } // 查看调用栈 断点1命中时,查看Call Stack: main() └─ AnnotationConfigApplicationContext.<init>() └─ AbstractApplicationContext.refresh() ├─ prepareRefresh() ├─ obtainFreshBeanFactory() ├─ prepareBeanFactory() ├─ postProcessBeanFactory() ├─ invokeBeanFactoryPostProcessors() ← 处理@Configuration、@Bean ├─ registerBeanPostProcessors() ← 注册BeanPostProcessor ├─ initMessageSource() ├─ initApplicationEventMulticaster() ├─ onRefresh() ├─ registerListeners() └─ finishBeanFactoryInitialization() ← 创建所有单例Bean // 使用条件断点 右键断点 → Condition: beanName.equals("userService") # 只在创建userService时暂停 // 使用日志断点 右键断点 → Evaluate and log: "Creating bean: " + beanName # 记录日志,不暂停程序 IoC容器启动流程:refresh()方法深度剖析 refresh()方法概览 // AbstractApplicationContext.refresh() // 这是Spring容器启动的核心方法,包含12个步骤 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1. 准备刷新上下文环境 prepareRefresh(); // 2. 获取BeanFactory(告诉子类刷新内部BeanFactory) ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3. 准备BeanFactory(配置BeanFactory的标准上下文) prepareBeanFactory(beanFactory); try { // 4. BeanFactory后置处理(允许子类对BeanFactory进行后置处理) postProcessBeanFactory(beanFactory); // 5. 调用BeanFactory后置处理器(执行所有BeanFactoryPostProcessor) invokeBeanFactoryPostProcessors(beanFactory); // 6. 注册Bean后置处理器(注册所有BeanPostProcessor) registerBeanPostProcessors(beanFactory); // 7. 初始化消息源(国际化) initMessageSource(); // 8. 初始化事件广播器 initApplicationEventMulticaster(); // 9. 刷新上下文(允许子类初始化特殊Bean,如内嵌Web容器) onRefresh(); // 10. 注册监听器 registerListeners(); // 11. 初始化所有单例Bean(非懒加载) finishBeanFactoryInitialization(beanFactory); // 12. 完成刷新(发布ContextRefreshedEvent事件) finishRefresh(); } catch (BeansException ex) { // 销毁已创建的Bean destroyBeans(); // 取消刷新 cancelRefresh(ex); throw ex; } finally { // 清理缓存 resetCommonCaches(); } } } 步骤1:prepareRefresh() - 准备刷新 protected void prepareRefresh() { // 1. 记录启动时间 this.startupDate = System.currentTimeMillis(); // 2. 设置容器状态 this.closed.set(false); // 未关闭 this.active.set(true); // 已激活 // 3. 初始化属性源(可由子类覆盖) initPropertySources(); // 4. 验证必需的属性 // 例如:getEnvironment().setRequiredProperties("JAVA_HOME"); getEnvironment().validateRequiredProperties(); // 5. 创建早期事件集合(在事件广播器准备好之前收集事件) if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // 6. 创建早期事件集合 this.earlyApplicationEvents = new LinkedHashSet<>(); } 作用: ✅ 记录启动时间(用于统计启动耗时) ✅ 设置容器状态(标记为激活状态) ✅ 初始化和验证环境变量 ✅ 准备事件监听器 步骤2:obtainFreshBeanFactory() - 获取BeanFactory protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 刷新BeanFactory(由子类实现) refreshBeanFactory(); // 返回BeanFactory return getBeanFactory(); } // AbstractRefreshableApplicationContext(XML配置) @Override protected final void refreshBeanFactory() throws BeansException { // 如果已有BeanFactory,先销毁 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { // 创建新的BeanFactory(DefaultListableBeanFactory) DefaultListableBeanFactory beanFactory = createBeanFactory(); // 设置序列化ID beanFactory.setSerializationId(getId()); // 定制BeanFactory(是否允许Bean覆盖、是否允许循环依赖) customizeBeanFactory(beanFactory); // 加载Bean定义(从XML文件) loadBeanDefinitions(beanFactory); // 保存BeanFactory引用 this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source", ex); } } // GenericApplicationContext(注解配置) @Override protected final void refreshBeanFactory() throws IllegalStateException { // 标记为已刷新 if (!this.refreshed.compareAndSet(false, true)) { throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts"); } // 设置序列化ID this.beanFactory.setSerializationId(getId()); } 对比: XML配置(AbstractRefreshableApplicationContext): └─ 每次refresh都创建新的BeanFactory └─ 从XML文件加载Bean定义 注解配置(GenericApplicationContext): └─ BeanFactory在构造方法中创建(只创建一次) └─ Bean定义通过注解扫描加载(在步骤5) 步骤3:prepareBeanFactory() - 准备BeanFactory protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 1. 设置类加载器 beanFactory.setBeanClassLoader(getClassLoader()); // 2. 设置SpEL表达式解析器 beanFactory.setBeanExpressionResolver( new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()) ); // 3. 添加属性编辑器注册器(用于类型转换) beanFactory.addPropertyEditorRegistrar( new ResourceEditorRegistrar(this, getEnvironment()) ); // 4. 添加BeanPostProcessor:ApplicationContextAwareProcessor // 作用:处理Aware接口回调(ApplicationContextAware、EnvironmentAware等) beanFactory.addBeanPostProcessor( new ApplicationContextAwareProcessor(this) ); // 5. 忽略以下Aware接口的依赖注入(由ApplicationContextAwareProcessor处理) beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // 6. 注册可解析的依赖(自动装配时使用) beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 7. 添加BeanPostProcessor:ApplicationListenerDetector // 作用:检测ApplicationListener类型的Bean并注册到事件广播器 beanFactory.addBeanPostProcessor( new ApplicationListenerDetector(this) ); // 8. 如果存在LoadTimeWeaver,添加编织支持 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 9. 注册默认的环境Bean if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties()); } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment()); } } 作用: ✅ 配置类加载器、表达式解析器 ✅ 添加BeanPostProcessor(处理Aware接口回调) ✅ 注册可解析的依赖(BeanFactory、ApplicationContext等) ✅ 注册环境Bean(Environment、System Properties等) 步骤5:invokeBeanFactoryPostProcessors() - 执行BeanFactory后置处理器 这是最复杂也是最重要的步骤之一,负责处理@Configuration、@Bean、@ComponentScan等注解 ...

2025-11-03 · maneng

并发设计模式与最佳实践

一、不可变对象模式 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并发编程系列完结 ...

2025-11-03 · maneng

如约数科科技工作室

浙ICP备2025203501号

👀 本站总访问量 ...| 👤 访客数 ...| 📅 今日访问 ...