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

多线程 线程池 并发

核心知识点

1. 线程与进程

  • 进程(Process):是操作系统资源分配的基本单位。每个进程都有独立的内存空间。

  • 线程(Thread):是 CPU 调度的基本单位,是程序执行流的最小单元。一个进程可以包含多个线程,线程共享进程的内存空间。

2. 并发与并行

  • 并发(Concurrency):指多个任务在同一时间段内交替执行,但在任一时刻只有一个任务在运行。

  • 并行(Parallelism):指多个任务在同一时刻同时执行。这需要多核 CPU 的支持。

3. 线程状态

一个线程的完整生命周期会经历 6 种状态:

  • NEW:线程被创建但未启动。

  • RUNNABLE:线程正在执行,或正在等待 CPU 调度。

  • BLOCKED:线程正在等待监视器锁(如 synchronized)进入同步代码块。

  • WAITING:线程无限期地等待另一个线程执行特定操作。

  • TIMED_WAITING:线程在指定的时间内等待。

  • TERMINATED:线程执行完毕或异常终止。

4. 线程安全问题

当多个线程同时访问和修改共享资源时,由于执行顺序不确定,可能导致数据不一致,这就是竞态条件(Race Condition)。解决线程安全问题的核心是同步(Synchronization)

5. 线程池(Thread Pool)

线程池是一种管理和复用线程的机制。它通过一个队列来存放任务,并用一组固定的线程来执行这些任务。

  • 优点

    • 降低开销:避免了频繁创建和销毁线程的资源消耗。

    • 资源管理:控制并发线程数,防止因线程过多而耗尽系统资源。

    • 提高响应速度:任务无需等待新线程创建,可立即执行。


高频面试题与解答

1. synchronizedvolatile 关键字的区别?

  • synchronized:提供互斥性可见性。它保证同一时刻只有一个线程可以访问同步代码块,并且在释放锁时,会将工作内存中的共享变量刷新到主内存,保证其他线程的可见性。

  • volatile:只提供可见性,不提供互斥性。它保证对一个 volatile 变量的读写操作都是直接在主内存中进行,确保多个线程看到的是同一个值。但它不保证原子性,不适合用于复合操作(如 i++)。

2. synchronizedReentrantLock 的区别?

  • synchronized

    • 是 Java 语言内置的关键字。

    • 使用简单,由 JVM 自动管理锁的获取和释放。

    • 是非公平锁。

  • ReentrantLock

    • java.util.concurrent.locks.Lock 接口的实现类。

    • 功能更丰富,提供了可中断、可限时、公平锁(可选)等功能。

    • 需要手动获取和释放锁,通常在 try-finally 块中确保释放,避免死锁。

3. 为什么说 volatile 不保证原子性?

volatile 确保了可见性,但不保证原子性。例如,i++ 实际上是三个操作:

  1. 读取 i 的值。

  2. i 进行加 1 操作。

  3. 将新值写入 i

在多线程环境下,一个线程读取 i 的值后,另一个线程可能已经完成了 i++ 操作,但第一个线程的写入操作会覆盖后一个线程的结果,导致数据错误。

4. 请解释一下 CAS?它解决了什么问题?

CAS(Compare-and-Swap) 是一种无锁算法。它包含三个操作数:

  1. 内存位置 V:需要更新的变量。

  2. 旧的预期值 A:假设该变量当前的值。

  3. 新的值 B:想要更新成的值。

CAS 的原子操作是:当且仅当内存位置 V 的值等于 A 时,才将 V 的值更新为 B。如果 V 的值不等于 A,则说明已被其他线程修改,更新失败。

它解决了在多线程下非阻塞地更新共享变量的问题,是许多并发工具(如 AtomicInteger)的底层实现。

5. 什么是死锁?如何避免?

死锁是指两个或多个线程在执行过程中,因争夺资源而造成相互等待,若无外力干涉,它们都将无法继续执行。

死锁的四个必要条件

  1. 互斥条件:资源不能被共享。

  2. 请求与保持:线程已持有资源,又请求新资源。

  3. 不剥夺:已分配的资源不能被强制剥夺。

  4. 循环等待:形成一个环形链,每个线程都在等待下一个线程释放资源。

避免策略:破坏这四个条件中的任意一个。最常用的方法是破坏循环等待条件,通过对资源进行排序,按顺序申请资源。

6. 使用线程池的好处是什么?ThreadPoolExecutor 的核心参数有哪些?

好处

  1. 降低资源消耗:通过线程复用,减少创建和销毁线程的开销。

  2. 提高响应速度:任务提交后立即执行,无需等待线程创建。

  3. 方便管理:统一管理线程,控制并发数量,防止资源耗尽。

核心参数

  • corePoolSize:核心线程数,线程池中始终保持的线程数量。

  • maximumPoolSize:最大线程数,线程池中允许的最大线程数量。

  • keepAliveTime:当线程数大于核心线程数时,多余的空闲线程的存活时间。

  • unitkeepAliveTime 的时间单位。

  • workQueue:任务队列,用于存放待执行的任务。

  • threadFactory:创建线程的工厂。

  • handler:拒绝策略,当任务队列已满且线程数达到最大值时,如何处理新任务。

7. RunnableCallable 有什么区别?

  • Runnable

    • run() 方法没有返回值。

    • 不能抛出受检异常。

  • Callable

    • call() 方法有返回值,通常是 Future<V>

    • 可以抛出异常。

  • 执行方式Runnable 通常由 Thread 执行;Callable 通常由 ExecutorService 提交,并通过 Future 对象获取结果。

8. CountDownLatchCyclicBarrier 的区别?

  • CountDownLatch倒计时门闩。它允许一个或多个线程等待,直到其他线程完成操作。它是一次性的,count 减到 0 后就不能再用了。

  • CyclicBarrier循环屏障。它允许多个线程相互等待,直到所有线程都到达同一个屏障点,然后它们可以一起继续执行。CyclicBarrier 可以重复使用

9. 什么是守护线程(Daemon Thread)?

守护线程是一种特殊的线程,它在后台为其他非守护线程提供服务。JVM 在所有非守护线程都结束后,会自动终止。例如,Java 的垃圾回收线程(GC)就是一个守护线程。

10. happens-before 原则是什么?

happens-before 原则定义了多线程环境下,一个操作的结果对另一个操作的可见性。它是 Java 内存模型(JMM)中的核心概念。

例如,如果操作 A happens-before 操作 B,那么操作 A 的结果对于操作 B 是可见的,且操作 A 的执行顺序在操作 B 之前。它为开发者提供了可见性保证,无需关心底层的重排序。

1. 阻塞(Blocking)与非阻塞(Non-blocking)的区别是什么?

  • 阻塞:当一个线程执行一个操作,如果该操作的资源没有准备好,它会挂起自身,让出 CPU 资源,直到资源就绪。例如,当线程试图获取一个被其他线程持有的 synchronized 锁时,它会进入阻塞状态。

  • 非阻塞:当一个线程执行操作时,如果资源没有准备好,它会立即返回失败,而不会挂起。例如,CAS(Compare-and-Swap)操作就是非阻塞的,它会不断重试直到成功。

2. 解释一下 AQS(AbstractQueuedSynchronizer)框架。

AQS 是一个同步器框架,它是 java.util.concurrent 包中许多同步工具(如 ReentrantLock, Semaphore, CountDownLatch 等)的底层核心。

  • 核心思想:AQS 维护了一个共享资源状态(state),以及一个FIFO 的等待队列。当线程尝试获取资源失败时,它会被封装成一个节点并进入等待队列。当持有资源的线程释放资源时,它会唤醒等待队列中的下一个线程。

  • 作用:它为同步器提供了统一的模板,开发者只需要实现获取和释放资源的抽象方法,就可以构建自己的同步器,而无需关心复杂的线程排队、唤醒等底层细节。

3. Semaphore(信号量)是什么?它有什么用?

Semaphore 是一个计数器。它可以控制同时访问特定资源的线程数量。

  • 工作原理Semaphore 维护一个许可证(permits)计数器。

    • acquire() 方法会消耗一个许可证。如果许可证数量为 0,线程就会被阻塞,直到有其他线程释放许可证。

    • release() 方法会释放一个许可证,并可能唤醒等待的线程。

  • 用途:常用于限制对某些共享资源的并发访问数,例如控制数据库连接池、线程池或并发下载数。

4. 什么是读写锁(ReentrantReadWriteLock)?它有什么优势?

读写锁是一种更高级的锁,它允许多个线程同时读取共享资源,但只允许一个线程写入共享资源。

  • 优势:在读多写少的并发场景中,读写锁能够显著提高系统的吞吐量,因为它允许多个读操作并行执行。如果使用 ReentrantLock,读操作也会相互阻塞,导致性能下降。

5. 什么是公平锁(Fair Lock)和非公平锁(Unfair Lock)?

  • 公平锁:按照线程请求锁的先后顺序来获取锁。在锁释放时,等待时间最长的线程将优先获取锁。这保证了所有线程都能获得执行机会,但会带来性能上的开销。

  • 非公平锁:当锁可用时,任何线程都可以尝试获取,不管队列中是否还有其他等待的线程。新来的线程可能会比队列中等待已久的线程先获得锁。它的性能通常比公平锁高。

ReentrantLock 默认是非公平锁。

6. ThreadLocal 是什么?它的工作原理是什么?

ThreadLocal 提供了一种线程本地存储的机制。它可以让每个线程都拥有一个独立的、互不影响的变量副本。

  • 工作原理ThreadLocal 内部维护了一个 ThreadLocalMap,这个 Map 的键是 ThreadLocal 对象本身,值是线程私有的变量副本。当线程调用 get() 方法时,它会从自己的 ThreadLocalMap 中获取对应的变量。

  • 用途:常用于在多线程环境下,为每个线程存储独立的数据库连接、用户信息等,避免线程安全问题。

7. synchronizedConcurrentHashMap 的区别?

这个问题是想考察你对悲观锁乐观锁的理解。

  • synchronized:是悲观锁,它通过锁住整个对象来保证线程安全。当一个线程访问时,其他所有线程都被阻塞。这在写操作频繁的场景下性能较差。

  • ConcurrentHashMap:采用分段锁CAS乐观锁机制。它只锁住哈希表中的一部分(一个桶),从而允许多个线程同时对不同桶进行读写操作,大大提高了并发性能。

8. 什么是 Fork/Join 框架?它有什么特点?

Fork/Join 是一个用于并行执行任务的框架,是 java.util.concurrent 包的一部分。它基于“分而治之”的思想。

  • 核心思想

    • Fork(拆分):将一个大的任务递归地拆分为多个足够小的子任务。

    • Join(合并):等待所有子任务执行完毕,然后将它们的结果合并。

  • 特点:它利用**工作窃取(work-stealing)**算法,让空闲的线程从其他线程的队列中“窃取”任务来执行,以充分利用 CPU 资源,提高效率。

9. 如何确保一个类是线程安全的?

要确保一个类是线程安全的,需要保证其状态在被多个线程访问时不会出现不一致。常见的方法有:

  1. 不可变(Immutable):将类设计为不可变类,所有字段都是 final,且没有任何方法可以修改其状态。

  2. 同步化:使用 synchronized 关键字或 ReentrantLock 等锁机制来同步对共享字段的访问。

  3. 使用原子类:使用 AtomicIntegerAtomicLong 等原子类来确保对变量的修改是原子性的。

  4. 使用线程安全集合:使用 ConcurrentHashMapCopyOnWriteArrayList 等线程安全的集合类。

10. StringStringBufferStringBuilder 哪个是线程安全的?

  • String不可变,因此是线程安全的。一旦创建,其值不能被改变。

  • StringBuffer:是线程安全的。它的所有公共方法都使用了 synchronized 关键字进行同步,适合在多线程环境下使用。

  • StringBuilder非线程安全的。它没有同步机制,性能比 StringBuffer 高,适合在单线程环境下使用。

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

相关文章:

  • 机器视觉学习-day08-图像缩放
  • MBA/EMBA毕业论文写作总结
  • 第20章|轻松实现远程控制
  • NumPy 2.x 完全指南【三十二】通用函数(ufunc)之数学运算函数
  • 面试tips--JVM(1)--对象分配内存的方式TLAB
  • CTFshow系列——命令执行web61-68
  • C++之多态篇
  • 君正T31学习(四)- MT7682+VLC出图
  • 【python】python进阶——as关键字
  • 程序代码篇---类
  • SpringCloud Alibaba Nacos 注册中心/配置中心
  • SpringBoot 配置文件在运维开发中的应用
  • 基于springboot的商业店铺租赁系统
  • 在 Vue 前端(Vue2/Vue3 通用)载入 JSON 格式的动图
  • 校园文化活动管理系统设计与实现(代码+数据库+LW)
  • web前端知识——第一阶段
  • 【buildroot】【1. Buildroot版本与Linux内核调试对应关系】
  • 基于SpringBoot的旅游景点推荐系统【2026最新】
  • 域名所有权变更,需要重新备案吗
  • Day16_【机器学习分类】
  • 软磁材料与硬磁材料
  • MTK Linux DRM分析(十九)- KMS drm_framebuffer.c
  • LeetCode 141.环形链表
  • 软考中级【网络工程师】第6版教材 第4章 无线通信网 (上)
  • 8.28 JS移动端事件
  • HTTP 范围请求:为什么你的下载可以“断点续传”?
  • 现在购买PCIe 5.0 SSD是否是最好的时机?
  • 嵌入式学习笔记--LINUX系统编程阶段--DAY02系统编程
  • 嵌入式学习日志————实验:串口发送串口发送+接收
  • 亚远景-ISO/PAS 8800:如何通过认证提升企业技术管理能力?