当前位置: 首页 > news >正文

Java 并发编程完整讲解

一、并发基础概念

1. 并发 vs 并行

  • 并发:多个任务交替执行,单核 CPU 切换线程,宏观同时跑,微观串行
  • 并行:多核 CPU 同时执行多个线程,真正同一时刻运行

2. 进程 & 线程

  • 进程:操作系统资源分配最小单位,独立内存、文件句柄,进程间隔离
  • 线程:进程内执行单元,共享进程堆内存,栈独立,切换开销远小于进程
  • JVM:一个 Java 程序启动就是一个进程,默认主线程main

3. 三大并发核心问题

  1. 可见性:一个线程修改共享变量,其他线程看不到
  2. 原子性:多步操作不可拆分,中途不能被其他线程打断(i++ 非原子)
  3. 有序性:CPU / 编译器指令重排,代码执行顺序和书写不一致

4. JMM Java 内存模型

JMM 规定线程、主内存、工作内存交互规则:

  1. 所有共享变量存在主内存
  2. 每个线程私有工作内存(缓存寄存器)
  3. 线程读写变量必须拷贝到工作内存,不能直接操作主存 三大关键字对应三大特性:
  • volatile:保证可见性 + 禁止指令重排,不保证原子性
  • synchronized:保证原子性 + 可见性 + 有序性
  • Lock锁:和 synchronized 语义一致,API 层面锁

二、Java 创建线程的 4 种方式

1. 继承 Thread 类

java

class MyThread extends Thread { @Override public void run() { System.out.println("线程执行"); } } // 使用 new MyThread().start();

缺点:单继承限制,无法再继承其他类

2. 实现 Runnable 接口

无返回值,无异常抛出,解耦线程与任务

java

Runnable task = () -> System.out.println("任务"); new Thread(task).start();

3. Callable + FutureTask

可抛出异常,获取线程执行结果

java

Callable<Integer> call = () -> 100; FutureTask<Integer> ft = new FutureTask<>(call); new Thread(ft).start(); Integer res = ft.get(); // 阻塞获取结果

4. 线程池 ExecutorService

手动创建线程开销大、不可控,统一线程池管理

java

ExecutorService pool = Executors.newFixedThreadPool(5); pool.submit(() -> {}); pool.shutdown();

线程核心方法

  • start():创建内核线程,调用 run (),只能调用一次
  • run():普通方法,直接调用不会新开线程
  • sleep(long):休眠,释放 CPU,不释放锁
  • wait():Object 方法,释放锁,进入等待队列,需 notify 唤醒
  • notify()/notifyAll():唤醒等待线程
  • join():主线程等待子线程执行完毕
  • interrupt():中断线程,设置中断标记,不直接停止

三、锁机制

1. synchronized 内置锁(悲观锁)

使用场景
  1. 修饰实例方法:锁当前对象this
  2. 修饰静态方法:锁类 Class 对象
  3. 修饰代码块:自定义锁对象

java

// 实例锁 public synchronized void test(){} // 代码块锁 synchronized (obj) {} // 静态锁 public static synchronized void test(){}
底层原理
  • 字节码:monitorenter/monitorexit
  • 对象头 Mark Word 存储锁标记:无锁→偏向锁→轻量级锁→重量级锁(锁膨胀)
  • 自动释放锁,异常也会释放,无需手动解锁
优缺点

优点:语法简单、自动释放、JVM 优化 缺点:阻塞不可中断、非公平锁、无法灵活控制

2. Lock 显式锁(java.util.concurrent.locks)

ReentrantLock 可重入锁

java

Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区 } finally { lock.unlock(); // 必须finally释放,防止死锁 }

相比 synchronized 优势:

  1. 可中断等待:lockInterruptibly()
  2. 尝试非阻塞获取锁:tryLock()
  3. 公平锁 / 非公平锁可选:new ReentrantLock(true)公平
  4. 多条件等待:多个 Condition 区分等待队列
ReadWriteLock 读写锁
  • ReentrantReadWriteLock:读共享、写互斥
  • 读多写少场景(缓存)提升并发;写锁阻塞所有读写
StampedLock 乐观读写锁

提供乐观读模式,无锁开销,适合高并发读场景

3. 锁分类

  1. 可重入锁:同一线程多次获取同一锁不会死锁(synchronized、ReentrantLock 都是)
  2. 公平锁:线程按等待顺序获取锁;非公平锁抢占,吞吐量更高
  3. 悲观锁:默认一定会竞争,加锁阻塞(synchronized、Lock)
  4. 乐观锁:无锁,版本号 CAS 判断冲突(Atomic、数据库 MVCC)
  5. 自旋锁:线程循环尝试获取锁,不挂起,短等待场景高效

四、volatile 关键字

作用

  1. 可见性:修改后强制刷新主存,其他线程缓存失效,立即读取主存
  2. 禁止指令重排:插入内存屏障,保证执行顺序

局限性

不保证原子性volatile int i; i++线程不安全

典型使用场景

  1. 状态标记(开关变量)

java

volatile boolean stop = false; while(!stop){}
  1. 双重检查锁 DCL 单例(防止指令重排导致半初始化对象)

java

public class Singleton { private volatile static Singleton instance; public static Singleton getInstance() { if(instance == null) { synchronized (Singleton.class) { if(instance == null) instance = new Singleton(); } } return instance; } }

五、CAS & 原子类(无锁并发)

CAS 比较并交换

乐观锁核心算法:V(内存值) == E(预期值) ? 更新为N : 失败重试CPU 硬件指令保证原子操作,无锁、无内核切换,性能高 问题:

  1. ABA 问题:值 A→B→A,CAS 无法感知变化 → 用AtomicStampedReference版本号解决
  2. 循环自旋消耗 CPU
  3. 只能单个变量原子操作

JUC 原子工具包 java.util.concurrent.atomic

  1. 基本类型:AtomicInteger、AtomicLong、AtomicBoolean

jav运行

AtomicInteger num = new AtomicInteger(0); num.incrementAndGet(); // i++原子操作
  1. 引用类型:AtomicReference、AtomicStampedReference(解决 ABA)
  2. 数组原子类:AtomicIntegerArray
  3. 对象字段更新器:AtomicIntegerFieldUpdater(节省对象内存)
  4. 累加器:LongAdder高并发下优于 AtomicLong,分段计数降低 CAS 竞争

六、线程间通信

1. wait / notify / notifyAll(Object)

前提:必须在 synchronized 同步块内

  • wait ():释放锁,阻塞等待,等待其他线程 notify
  • notify ():随机唤醒一个等待线程
  • notifyAll ():唤醒全部等待线程 标准生产者消费者模型基础

2. Condition(Lock 配套)

替代 wait/notify,支持多等待队列,精准唤醒指定线程

java

Condition full = lock.newCondition(); Condition empty = lock.newCondition(); empty.await(); // 等价wait full.signal(); // 等价notify

3. CountDownLatch 减法计数器

等待 N 个线程完成后主线程再执行

java

CountDownLatch latch = new CountDownLatch(5); latch.countDown(); // 计数-1 latch.await(); // 阻塞直到计数0

4. CyclicBarrier 循环屏障

多个线程互相等待,全部到达屏障点再一起执行,可重复使用

java

CyclicBarrier barrier = new CyclicBarrier(3); barrier.await(); // 3个线程都到这里才放行

5. Semaphore 信号量

控制并发线程数量,限流工具

java

Semaphore sem = new Semaphore(5); // 最多5个并发 sem.acquire(); // 获取许可 sem.release(); // 释放许可

七、线程池

1. 为什么要用线程池

  • 避免频繁创建销毁线程,降低开销
  • 控制并发数量,防止 CPU 耗尽
  • 统一管理线程,提供定时、延迟、任务拒绝策略

2. ThreadPoolExecutor 七大参数

java

public ThreadPoolExecutor( int corePoolSize, // 核心线程数(常驻) int maximumPoolSize, // 最大线程数 long keepAliveTime, // 非核心线程空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueue<Runnable> workQueue, // 阻塞队列 ThreadFactory threadFactory, // 线程工厂 RejectedExecutionHandler handler // 拒绝策略 )

执行流程:

  1. 任务进来,核心线程未满 → 创建核心线程执行
  2. 核心线程满,任务放入阻塞队列
  3. 队列满,创建非核心线程直到达到最大线程数
  4. 总线程达上限,执行拒绝策略

3. 4 种拒绝策略

  1. AbortPolicy(默认):直接抛 RejectedExecutionException
  2. DiscardPolicy:丢弃当前任务,无异常
  3. DiscardOldestPolicy:丢弃队列最老任务,重试提交
  4. CallerRunsPolicy:交给提交任务主线程执行,限流

4. 内置工具类线程池

  1. newFixedThreadPool(n):固定线程,无界队列,任务堆积 OOM
  2. newSingleThreadExecutor():单线程,无界队列
  3. newCachedThreadPool():可缓存,无核心线程,最大线程无限,线程暴涨 OOM
  4. newScheduledThreadPool():定时 / 延迟任务线程池 生产规范:手动 new ThreadPoolExecutor,自定义队列、线程数、拒绝策略

5. ScheduledExecutorService 定时任务

替代 Timer(单线程,任务失败全部阻塞),多线程定时执行

八、并发容器 JUC 集合

1. 阻塞队列 BlockingQueue

生产者消费者核心,队列满 / 空自动阻塞

  • ArrayBlockingQueue:数组有界阻塞队列,一把锁
  • LinkedBlockingQueue:链表无界 / 有界,读写分离两把锁,并发高
  • SynchronousQueue:无容量,插入必须等待取出
  • PriorityBlockingQueue:优先级阻塞队列
  • DelayQueue:延迟队列,定时任务底层

2. 并发 Map

  • HashMap:线程不安全,并发 put 会链表循环死循环
  • Hashtable:全表 synchronized,性能极差
  • ConcurrentHashMap(推荐):分段锁 / 数组 CAS+synchronized,高并发安全
    • JDK7:分段 Segment 锁
    • JDK8:取消分段,数组节点 CAS,链表转红黑树,锁只锁头节点

3. 其他并发集合

  • CopyOnWriteArrayList/CopyOnWriteArraySet:写时复制,读无锁,写加锁复制新数组,读多写少场景

九、并发常见问题

1. 死锁

四个必要条件

  1. 互斥:资源独占
  2. 持有并等待:持有 A,等待 B
  3. 不可剥夺:资源不能强制抢走
  4. 循环等待:线程 1 等线程 2 资源,线程 2 等线程 1 资源

解决死锁:破坏任一条件,统一锁获取顺序、设置超时、避免嵌套锁

2. 线程安全总结

不安全场景:多线程读写共享变量,无同步措施 安全方案优先级:

  1. 无共享(栈局部变量)
  2. 不可变对象 final
  3. 原子类 CAS
  4. Lock 锁
  5. synchronized

十、并发工具完整使用场景总结

表格

工具用途
volatile状态标记、DCL 单例
synchronized简单同步、少量竞争
ReentrantLock公平锁、可中断、多条件
ReadWriteLock读多写少缓存
AtomicXXX单个变量原子计数
LongAdder高并发统计
CountDownLatch等待多线程完成
CyclicBarrier多线程同步等待
Semaphore接口限流
ThreadPoolExecutor异步任务执行
ConcurrentHashMap高并发键值存储
BlockingQueue生产者消费者

十一、并发编程最佳实践

  1. 尽量缩小同步代码块范围,降低锁持有时间
  2. 优先使用局部变量、不可变对象,避免共享
  3. 高并发计数用 LongAdder 代替 AtomicLong
  4. 生产禁止 Executors 快捷创建线程池,手动定义 ThreadPoolExecutor
  5. Lock 锁必须放在 try-finally 中释放
  6. 双重检查单例必须加 volatile
  7. 避免嵌套锁,防止死锁
  8. 线程池合理设置核心线程、队列长度,自定义拒绝策略
  9. 读多写少场景使用 CopyOnWrite、读写锁
  10. 不依赖线程优先级、yield,调度不可控
http://www.jsqmd.com/news/1196316/

相关文章:

  • Agent Island:把 Claude Code / Codex 的会话状态塞进刘海儿屏
  • 【模拟赛总结】20260714 模拟赛总结
  • 百达翡丽中国官方售后服务中心|服务电话及详细地址权威信息通告(2026年7月更新) - 百达翡丽官方售后中心
  • 抖店一键下单软件真能自动统计利润?全套功能详解 - 电商分享
  • 芝柏官方售后服务中心详细网点地址与热线实地考察报告_多信源验证(2026年7月最新) - 亨得利官方服务中心
  • 电动车专线托运选哪个便宜又科普?电动车托运水太深?慧寄侠整车直达,260元起不拆电池真香! - 快递物流资讯
  • 【数据挖掘实战】从购物篮到推荐系统:Apriori与FP-Growth算法Python全解析
  • MATLAB版Chan算法TDOA定位实现:含三基站/多基站代码+实操视频
  • 抖店一件代发实操:一键下单工具运费模板、发货地址完整设置教程 - 电商分享
  • 在线一键去水印工具推荐:手机电脑微信小程序三端可用工具横评 - 科技热点发布
  • 从赛题到实战:运动目标控制与自动追踪系统的核心设计思路解析
  • 劳力士中国官方售后服务中心|地址与联系电话权威信息通知(2026年7月更新) - 劳力士服务中心
  • 伯爵中国官方售后服务中心|服务热线及官方维修地址权威信息声明(2026年7月最新) - 亨得利钟表维修中心
  • 衡阳市k式板房厂商选购指南 本地临建采购实用参考 - 热点品牌推荐
  • GOD:LLM 智能体社会的实时“控制室“——不止于观察,更要介入
  • Python asyncio 数据爬取:并发 100 个请求的正确姿势
  • 端侧大模型部署实战:突破内存、算力与碎片化三大瓶颈
  • 2026年除甲醛避坑指南:重庆源头厂家怎么选才靠谱? - 资讯纵览
  • C++/OpenCV 配置进阶:从“无法打开源文件”到正确引入opencv_contrib模块
  • Python入门实战:环境搭建、核心语法与首个项目开发指南
  • 从数学直觉到 AI 研究工程师:一本颠覆传统的开源自学教材全解析
  • 7.15做题记录(1)
  • 2026年7月最新重庆浪琴官方售后客户服务电话及线下网点地址 - 浪琴服务中心
  • TUSB8044 USB 3.1集线器寄存器配置实战:从原理到扩展坞设计
  • 2026年酸性清洗剂品牌厂商选型实用参考指南 - 热点品牌推荐
  • 2026年河北课程防工具适配本地需求选购指南 - 热点品牌推荐
  • 商城网站搭建平台哪个好,微商城和PC商城到底哪里不同
  • AI 指标异常根因分析:多维度关联比人工穷举快在哪
  • 船用割渔网刀具生产厂家选型采购及场景适配实用指南 - 热点品牌推荐
  • CBCX怎么样?一次围绕服务边界说明的理性复盘