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

【Java并发编程】概念与核心问题、线程核心、并发控制、线程池、并发容器、并发问题

文章目录

  • 前言
    • 1. 并发编程基础:概念与核心问题
      • 1.1 核心概念辨析
      • 1.2 并发编程核心问题(3大根源)
    • 2. 线程核心:生命周期与操作
      • 2.1 线程生命周期(JDK 8+ 6种状态)
      • 2.2 线程核心操作(创建与控制)
        • 2.2.1 线程创建3种方式
        • 2.2.2 线程控制常用方法
    • 3. 并发控制核心技术:锁与同步
      • 3.1 synchronized:内置锁(隐式锁)
        • 3.1.1 用法场景
        • 3.1.2 核心特性
      • 3.2 Lock:显式锁(JUC包)
        • 3.2.1 标准用法(必须在finally中释放锁)
        • 3.2.2 高级特性
      • 3.3 锁对比与选型
    • 4. 线程池:线程管理的最佳实践
      • 4.1 线程池核心原理与参数
        • 4.1.1 核心参数(7个)
        • 4.1.2 4种拒绝策略
      • 4.2 常见线程池对比(Executors工具类)
      • 4.3 线程池实战注意事项
    • 5. 并发容器:线程安全的数据存储
    • 6. 原子类与CAS:无锁并发控制
      • 6.1 原子类分类与示例
        • 6.1.1 代码示例(AtomicInteger)
      • 6.2 CAS核心原理
    • 7. 线程通信:协同与同步机制
    • 8. 常见并发问题与解决方案
      • 8.1 死锁(Deadlock)
      • 8.2 线程安全问题(数据错乱)
      • 8.3 上下文切换开销
    • 9. 并发编程实战选型指南
    • 10. 总结

前言

若对您有帮助的话,请点赞收藏加关注哦,您的关注是我持续创作的动力!有问题请私信或联系邮箱:funian.gm@gmail.com

在多核CPU时代,“充分利用硬件资源提升程序效率”是开发核心需求。单线程程序无法发挥多核优势,而Java并发编程通过多线程协同执行,实现“同一时间处理多个任务”,广泛应用于服务器、中间件等高频场景。但并发也带来线程安全、死锁等问题,需通过标准化工具与规范解决。本文基于JDK 8+,拆解Java并发编程的核心原理与实战方案。

1. 并发编程基础:概念与核心问题

1.1 核心概念辨析

概念定义举例
并发(Concurrency)同一时间段内,多个任务交替执行(单核CPU通过时间片切换实现)单CPU上多线程处理请求
并行(Parallelism)同一时刻,多个任务同时执行(依赖多核CPU)多核CPU上多线程同时计算
线程(Thread)进程内的执行单元,共享进程资源,轻量级(切换开销远低于进程)Java程序的main方法对应主线程
进程(Process)操作系统资源分配的基本单位,进程间资源独立,重量级一个Java.exe对应一个进程
线程安全多线程并发访问时,程序仍能保证数据正确性、执行结果一致ConcurrentHashMap的put操作

1.2 并发编程核心问题(3大根源)

并发问题本质是“多线程对共享资源的无序访问”,根源可归结为3点:

  1. 原子性:一个操作或多个操作,要么全部执行且不被打断,要么全不执行(如i++实际是read→modify→write三步,非原子);
  2. 可见性:一个线程修改共享变量后,其他线程能立即看到修改结果(CPU缓存导致修改暂存本地,未同步到主内存);
  3. 有序性:程序执行顺序与代码顺序一致(JVM指令重排序优化可能打乱顺序,如int a=0; boolean flag=false;可能先执行flag=true)。

2. 线程核心:生命周期与操作

2.1 线程生命周期(JDK 8+ 6种状态)

Java线程生命周期由Thread.State枚举定义,状态转换如下:

graph LRA[新建(NEW)] --> B[就绪(RUNNABLE)]B --> C[运行(RUNNING)]C --> D[阻塞(BLOCKED)]C --> E[等待(WAITING)]C --> F[超时等待(TIMED_WAITING)]D --> BE --> BF --> BC --> G[终止(TERMINATED)]
状态含义进入方式退出方式
NEW线程已创建,未调用start()new Thread()调用start()
RUNNABLE线程就绪(等待CPU调度)或正在运行(CPU执行中)start()、阻塞/等待状态结束CPU调度执行、进入阻塞/等待状态
BLOCKED线程等待锁(如synchronized未获取到锁)竞争synchronized锁失败获取到synchronized锁
WAITING线程无限期等待(需其他线程唤醒)wait()join()LockSupport.park()notify()notifyAll()LockSupport.unpark()
TIMED_WAITING线程超时等待(到时间自动唤醒)sleep(ms)wait(ms)join(ms)超时时间到、被唤醒
TERMINATED线程执行完成或异常终止run()执行完、抛出未捕获异常无(线程生命周期结束)

2.2 线程核心操作(创建与控制)

2.2.1 线程创建3种方式
创建方式核心原理优点缺点代码示例
继承Thread类重写run()方法,直接操作线程对象实现简单,可直接访问Thread的方法单继承限制,代码耦合高class MyThread extends Thread { public void run() {} } new MyThread().start();
实现Runnable接口重写run(),传入Thread构造器无单继承限制,代码解耦无法直接获取线程执行结果(需额外处理)Runnable runnable = () -> {}; new Thread(runnable).start();
实现Callable接口重写call(),结合FutureTask获取结果可获取执行结果,支持异常抛出实现稍复杂,需FutureTask包装Callable<Integer> callable = () -> 1; FutureTask<Integer> ft = new FutureTask<>(callable); new Thread(ft).start(); ft.get();

在这里插入图片描述

2.2.2 线程控制常用方法
方法名作用注意事项
start()启动线程,将线程状态从NEW转为RUNNABLE(JVM调用run()不可重复调用(重复调用抛IllegalThreadStateException)
run()线程执行逻辑(业务代码)直接调用仅为普通方法,不会启动新线程
sleep(long ms)让线程进入TIMED_WAITING,不释放锁(synchronized/Lock)需捕获InterruptedException
wait()让线程进入WAITING,释放synchronized锁(仅在同步块中调用)需配合notify()唤醒,需捕获InterruptedException
join()等待当前线程执行完成(如主线程等待子线程结束)需捕获InterruptedException
interrupt()中断线程(设置中断标志,不直接终止线程)需通过isInterrupted()判断中断状态

3. 并发控制核心技术:锁与同步

控制并发的核心是“保证共享资源的有序访问”,Java提供内置锁(synchronized)显式锁(Lock) 两类方案。

在这里插入图片描述

3.1 synchronized:内置锁(隐式锁)

synchronized是Java原生锁,基于“对象监视器(Monitor)”实现,无需手动释放,属于可重入、非公平锁

3.1.1 用法场景
锁定场景核心逻辑代码示例
锁定实例方法锁对象为当前类实例(this)public synchronized void method() {}
锁定静态方法锁对象为当前类的Class对象public static synchronized void method() {}
锁定代码块锁对象为自定义对象(如Object)synchronized (lockObj) { // 业务逻辑 }
3.1.2 核心特性
  • 可重入:同一线程可多次获取同一把锁(如同步方法调用同步方法,不会死锁);
  • 隐式释放:线程退出同步块/方法时,JVM自动释放锁(即使抛出异常);
  • 非公平:线程获取锁的顺序不保证(新线程可能优先于等待线程获取锁)。

3.2 Lock:显式锁(JUC包)

java.util.concurrent.locks.Lock是JDK 5引入的显式锁,需手动lock()获取锁、unlock()释放锁,支持更灵活的并发控制(如公平锁、超时获取)。核心实现类是ReentrantLock(可重入锁)。

3.2.1 标准用法(必须在finally中释放锁)
Lock lock = new ReentrantLock(); // 默认非公平锁,可传true设为公平锁
try {lock.lock(); // 获取锁(阻塞直到获取成功)// 业务逻辑(操作共享资源)
} finally {lock.unlock(); // 释放锁(避免异常导致锁泄漏)
}
3.2.2 高级特性
  • 公平锁支持:构造器传true,线程按等待顺序获取锁(避免饥饿,但性能略低);
  • 超时获取锁tryLock(long time, TimeUnit unit),超时未获取返回false(避免死等);
  • 可中断获取lockInterruptibly(),获取锁时可被interrupt()中断;
  • 条件变量:通过newCondition()获取Condition对象,实现精细化线程通信(如生产者-消费者)。

3.3 锁对比与选型

对比维度synchronizedReentrantLock
锁获取/释放隐式(JVM自动)显式(手动lock()/unlock())
公平性仅非公平锁支持公平/非公平(构造器指定)
超时获取不支持支持(tryLock(ms))
中断支持不支持支持(lockInterruptibly())
条件变量仅1个(通过wait()/notify())多个(通过newCondition())
锁状态查询不支持支持(isLocked()、hasQueuedThreads())
性能(高并发)JDK 6+优化后与ReentrantLock接近略高(灵活控制锁粒度)
适用场景简单同步场景(如单方法/代码块)复杂场景(公平锁、超时、多条件通信)

在这里插入图片描述
在这里插入图片描述

4. 线程池:线程管理的最佳实践

频繁创建/销毁线程会产生大量开销(上下文切换、内存占用),线程池通过“预先创建线程、复用线程”降低开销,是Java并发编程的“标配工具”。

4.1 线程池核心原理与参数

Java线程池核心类是ThreadPoolExecutor,其工作流程:

  1. 线程池初始化时创建核心线程;
  2. 任务提交时,优先使用核心线程执行;
  3. 核心线程满时,任务存入阻塞队列;
  4. 队列满时,创建非核心线程执行;
  5. 总线程数达最大线程数且队列满时,触发拒绝策略。
4.1.1 核心参数(7个)
参数名含义示例值作用说明
corePoolSize核心线程数(线程池长期维持的线程数)5任务提交时优先使用核心线程,即使空闲也不销毁(除非allowCoreThreadTimeOut=true)
maximumPoolSize最大线程数(核心线程+非核心线程的总数上限)10阻塞队列满时,最多可创建的线程数
keepAliveTime非核心线程空闲时间(超时后销毁)60L释放空闲资源,避免内存浪费
unitkeepAliveTime的时间单位TimeUnit.SECONDS配合keepAliveTime使用
workQueue阻塞队列(存储等待执行的任务)LinkedBlockingQueue缓冲任务,避免线程数骤增
threadFactory线程工厂(创建线程的工具类)Executors.defaultThreadFactory()统一设置线程名称、优先级等
handler拒绝策略(任务无法执行时的处理方式)AbortPolicy保护线程池,避免任务无限堆积
4.1.2 4种拒绝策略
拒绝策略类核心逻辑适用场景
AbortPolicy(默认)抛出RejectedExecutionException严格场景(不允许任务丢失,需感知异常)
CallerRunsPolicy由提交任务的线程(如主线程)执行并发量低,允许主线程临时处理任务
DiscardPolicy直接丢弃任务,不抛异常非核心任务(如日志收集,允许丢失)
DiscardOldestPolicy丢弃队列中最旧的任务,再提交新任务任务有优先级,新任务比旧任务重要

4.2 常见线程池对比(Executors工具类)

Executors提供4种默认线程池,但部分存在隐患(如OOM),需谨慎使用。

线程池类型核心参数配置优点缺点适用场景
FixedThreadPoolcorePoolSize=maximumPoolSize,队列无界线程数固定,避免线程膨胀队列无界(LinkedBlockingQueue),任务过多易OOM固定并发量的场景(如服务器接收请求)
CachedThreadPoolcorePoolSize=0,maximumPoolSize=Integer.MAX_VALUE,队列同步移交线程自动扩容/回收,灵活适应任务量最大线程数无界,高并发易创建过多线程导致OOM短期、轻量级任务(如临时计算)
ScheduledThreadPoolcorePoolSize固定,支持定时/延迟执行支持定时任务,线程复用长期定时任务需注意线程泄漏定时任务(如定时清理缓存、日志归档)
SingleThreadExecutorcorePoolSize=maximumPoolSize=1,队列无界单线程执行,保证任务顺序队列无界,任务过多易OOM;单线程故障影响所有任务需顺序执行的任务(如文件写入、单线程消费队列)

重要建议:避免使用Executors默认线程池,优先手动创建ThreadPoolExecutor,明确核心参数(尤其是阻塞队列容量),避免OOM风险。

在这里插入图片描述

在这里插入图片描述

4.3 线程池实战注意事项

  1. 合理设置核心参数
    • CPU密集型任务(如计算):核心线程数=CPU核心数+1(减少上下文切换);
    • I/O密集型任务(如DB查询、网络请求):核心线程数=CPU核心数×2(利用CPU空闲时间);
  2. 避免线程泄漏:确保任务中无无限循环、死锁,避免线程长期占用;
  3. 监控线程池状态:通过getActiveCount()(活跃线程数)、getQueue().size()(队列任务数)监控,及时调整参数;
  4. 优雅关闭线程池:用shutdown()(等待任务执行完)或shutdownNow()(强制中断任务),避免直接kill进程。

5. 并发容器:线程安全的数据存储

普通容器(如ArrayList、HashMap)线程不安全,JUC包提供并发容器,解决多线程下的数据安全问题。

并发容器类对应普通容器核心实现原理线程安全级别适用场景
ConcurrentHashMapHashMapJDK 8+:CAS+synchronized(分段锁优化)读写并发安全高并发键值对存储(如缓存、配置)
CopyOnWriteArrayListArrayList读写分离(写时复制数组,读不加锁)读多写少安全读频繁、写极少的场景(如配置列表)
CopyOnWriteArraySetHashSet基于CopyOnWriteArrayList实现读多写少安全读频繁的去重场景
ConcurrentLinkedQueueLinkedList(队列)无锁CAS实现(链表结构)高并发安全无界并发队列(如任务传递)
ArrayBlockingQueue数组队列ReentrantLock+Condition实现高并发安全有界队列(如生产者-消费者模型)
LinkedBlockingQueue链表队列ReentrantLock+Condition实现高并发安全可配置有界/无界队列(如线程池任务队列)

对比普通容器:并发容器通过“锁优化(如分段锁)、无锁CAS、读写分离”实现线程安全,性能远高于Collections.synchronizedXXX()包装的普通容器。

在这里插入图片描述

6. 原子类与CAS:无锁并发控制

原子类基于“CAS(Compare and Swap,比较并交换)”实现无锁并发控制,避免锁的上下文切换开销,适用于简单共享变量的原子操作。

6.1 原子类分类与示例

原子类类别核心类功能示例适用场景
原子基本类型AtomicInteger、AtomicLongincrementAndGet()(原子自增)、getAndSet()计数器(如接口调用次数、任务完成数)
原子引用类型AtomicReference、AtomicStampedReferencecompareAndSet()(原子更新引用)原子更新对象引用(如原子修改POJO属性)
原子数组AtomicIntegerArray、AtomicLongArraygetAndAdd(int i, int delta)(原子更新数组元素)原子操作数组元素(如并发修改数组计数)
原子字段更新器AtomicIntegerFieldUpdaterupdateAndGet(obj, func)(原子更新对象字段)不修改类源码,原子更新对象的非静态字段
6.1.1 代码示例(AtomicInteger)
public class AtomicDemo {private static final AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {// 多线程原子自增Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count.incrementAndGet(); // 原子i++,替代非原子的count++}});Thread t2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {count.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get()); // 输出:2000(无线程安全问题)}
}

6.2 CAS核心原理

CAS是一种乐观锁机制,核心逻辑:

  1. 输入参数:内存地址V、预期值A、新值B;
  2. 执行逻辑:若内存地址V的值等于预期值A,则将V的值更新为B,返回true;否则不更新,返回false;
  3. 循环重试:若CAS失败,线程自旋重试(无锁等待,避免上下文切换)。

CAS问题:

  • ABA问题:内存值从A→B→A,CAS误判为未修改(解决方案:AtomicStampedReference加版本号);
  • 自旋开销:高并发下CAS频繁失败,自旋导致CPU占用过高(解决方案:限制自旋次数、搭配锁);
  • 只能原子操作单个变量:需原子操作多个变量时,需用AtomicReference包装对象。

7. 线程通信:协同与同步机制

多线程需通过“通信”实现协同(如生产者生产后通知消费者消费),Java提供5种核心通信方式。

通信方式核心类/方法原理适用场景代码示例核心片段
wait()/notify()Object类方法(需配合synchronized)线程等待时释放锁,唤醒后重新竞争锁简单的线程间通知(如单生产者-单消费者)synchronized (lock) { lock.wait(); } / lock.notify();
ConditionLock的newCondition()基于Lock实现,支持多条件分组通知复杂通知(如多生产者-多消费者,分组唤醒)Condition cond = lock.newCondition(); cond.await(); cond.signal();
CountDownLatchJUC包类(倒计时门闩)计数器减至0时,唤醒所有等待线程等待多个线程完成(如主线程等所有子线程执行完)CountDownLatch latch = new CountDownLatch(2); latch.countDown(); latch.await();
CyclicBarrierJUC包类(循环栅栏)等待指定数量线程到达栅栏后,共同执行多线程分阶段执行(如所有线程准备好后开始计算)CyclicBarrier barrier = new CyclicBarrier(2); barrier.await();
SemaphoreJUC包类(信号量)控制同时访问资源的线程数(许可证机制)限流(如控制并发访问数据库的线程数)Semaphore sem = new Semaphore(5); sem.acquire(); sem.release();

8. 常见并发问题与解决方案

8.1 死锁(Deadlock)

  • 产生条件:资源互斥、持有并等待、不可剥夺、循环等待;
  • 解决方案
    1. 破坏循环等待:按固定顺序获取锁(如按锁对象的hashCode排序);
    2. 破坏持有并等待:一次性获取所有锁(如用Lock.tryLock()批量获取);
    3. 定时释放锁:用tryLock(ms)避免无限等待;
    4. 监控死锁:通过jstack <PID>命令查看线程堆栈,定位死锁线程。

8.2 线程安全问题(数据错乱)

  • 产生原因:共享变量未加同步,违反原子性/可见性/有序性;
  • 解决方案
    1. 用synchronized/Lock保证原子性与可见性;
    2. 用volatile修饰共享变量(保证可见性与有序性,不保证原子性);
    3. 用原子类(AtomicXXX)实现原子操作;
    4. 用并发容器替代普通容器。

8.3 上下文切换开销

  • 产生原因:CPU切换线程执行时,需保存/恢复线程状态(如程序计数器、寄存器);
  • 解决方案
    1. 合理设置线程池大小,避免线程过多;
    2. 用无锁编程(CAS、原子类)减少锁竞争;
    3. 用协程(如Project Loom的VirtualThread)替代线程,降低切换开销。

9. 并发编程实战选型指南

业务需求推荐技术排除技术
简单同步代码块/方法synchronizedReentrantLock(过度设计)
复杂同步(公平锁、多条件)ReentrantLock+Conditionsynchronized(功能不足)
高并发线程管理手动创建ThreadPoolExecutorExecutors默认线程池(易OOM)
读多写少的列表存储CopyOnWriteArrayListArrayList(线程不安全)、Vector(性能低)
高并发键值对存储ConcurrentHashMapHashMap(线程不安全)、Hashtable(性能低)
简单计数器AtomicIntegersynchronized(开销高)
等待多个线程完成CountDownLatchjoin()(无法复用,灵活性低)
限流控制Semaphore手动计数(易出错)

10. 总结

Java并发编程的核心是“在利用多核优势的同时,保证线程安全与效率”,关键要点可总结为:

  1. 基础核心:理解线程生命周期、原子性/可见性/有序性三大问题;
  2. 同步工具:synchronized(简单场景)、Lock(复杂场景)、原子类(无锁场景);
  3. 线程管理:线程池是最佳实践,需手动配置核心参数,避免默认实现;
  4. 数据存储:并发容器(ConcurrentHashMap、CopyOnWriteArrayList)替代普通容器;
  5. 线程通信:根据场景选wait/notify、Condition、CountDownLatch等工具;
  6. 问题排查:用jstack/jconsole监控线程状态,定位死锁、线程安全问题。

掌握Java并发编程,不仅能提升程序性能,更能应对高并发场景(如服务器、中间件)的开发需求,是Java高级工程师的核心技能之一。

http://www.dtcms.com/a/478943.html

相关文章:

  • C++模板编程实战记录:SFINAE核心技术深度掌握
  • Spring Boot项目的常用依赖有哪些?
  • 保姆级教程 | ASE学习过程记录分析
  • 网站如何留言免费网站seo排名优化
  • 运维视角:SpringBootWeb框架全解析
  • Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
  • 【组队学习】Post-training-of-LLMs TASK01
  • 涉县网站网络推广培训哪里好
  • Jenkins自动化配置--CICD流水线
  • 网站建设etw深圳租赁住房和建设局网站
  • 人力网站建设的建议wordpress加百度广告代码出问题
  • Mozilla 项目
  • 今日行情明日机会——20251013
  • 关于解决js中MediaRecorder录制的webm视频没有进度条的问题
  • 红日靶场(二)学习过程详细记录
  • 【多线程】门栓/闭锁(Latch/CountDownLatch)
  • [1-02-02].[第01章:HTML + CSS
  • 手机必备网站软件技术专科生的出路
  • 网站空间续费一年多少钱怎么弄推广广告
  • 一个做任务的网站如何绑定域名wordpress
  • 当ubuntu 系统的IP地址修改之后,gitlab服务应该如何修改?
  • 怎么做自己的公司网站本地服务器 wordpress
  • 网站制作 优帮云做淘宝客网站需要做后台吗
  • xsync.sh分发脚本和命令执行脚本
  • 深圳高端网站设计公司大连网站建设免费
  • mysql DATE_SUB函数 对日期或时间进行减法运算
  • 企业微信网站开发公司网易企业邮箱怎么找回密码
  • 力扣热题100p128最长连续序列
  • 【LeetCode热题100(42/100)】将有序数组转换为二叉搜索树
  • google网站建设网站开发答辩ppt