博客园
个人中心
Java 多线程 面试题
2025-01-08 19:29:25 发布
12 浏览
页面报错/反馈
已
收藏
点赞
并发编程三要素?
三要素主要包括可见性、原子性和有序性
可见性:是指一个线程对共享变量的修改能够被其他线程立即看到的特性。
原子性:是指一个或多个操作要么全部执行成功,要么全部执行失败,不会被其他因素打断。
有序性:是指程序执行的顺序必须符合预期,不能出现乱序的情况。
同步方法和同步块哪个是更好的选择?
如果业务需求简单,可以倾向于使用同步方法,因为它更简单直观。但如果需要更细粒度的锁控制,或者想要提高性能和减少死锁风险,同步块是更好的选择。
谈谈原子性?哪些使用到了?
原子性:是指一个或多个操作要么全部执行成功,要么全部执行失败,且这个执行过程不会被其他线程打断或干扰。
实现方式:
使用synchronized关键字:
通过在方法或代码块上使用synchronized关键字,可以确保同一时间只有一个线程能够执行该方法或代码块,从而实现原子性。但这种方式可能会导致线程阻塞和性能下降。
使用原子类:
Java提供了java.util.concurrent.atomic包中的一系列原子类,如AtomicInteger、AtomicLong、AtomicBoolean等。这些类中的方法都是原子操作,内部使用了CAS(Compare and Swap)等底层机制来确保操作的原子性。这种方式比使用synchronized关键字更高效,因为它不会导致线程阻塞。
使用Lock接口及其实现类:
Java还提供了Lock接口及其实现类(如ReentrantLock)来提供比synchronized更灵活的锁机制。通过显式地加锁和解锁,可以确保操作的原子性。但这种方式需要程序员自己管理锁的生命周期,增加了代码的复杂性。
谈谈可见性?哪些使用到了?
可见性:是指当一个线程修改了共享变量的值时,这个新值对其他线程来说是可以立即得知的。
可见性使用情况:
volatile关键字
synchronized关键字
final关键字
Locks和Condition
原子变量类
线程的启动和结束
谈谈有序性?举一个例子?
有序性:是指多线程环境下,程序执行顺序的一种保障机制。
什么是线程池?
线程池:是一种重用线程的机制,它可以在需要时创建线程,而不是每次都创建新的线程。
线程池核心参数:最大线程数、拒绝策略、核心线程数、任务队列、线程空闲时间
常见类型:固定大小线程池(FixedThreadPool)、缓存线程池(CachedThreadPool)、单线程池(SingleThreadPool)、定时线程池(ScheduledThreadPool)、工作窃取线程池
线程池有哪些创建方式?
使用Executors工厂类
直接使用ThreadPoolExecutor类
使用Executors工厂方法的变体
使用ForkJoinPool
谈谈四种线程池的创建?
Executors.newCachedThreadPool:创建一个可根据需要创建新线程的线程池,并允许自定义线程工厂。
Executors.newFixedThreadPool:创建一个可重用固定线程数的线程池,并允许自定义线程工厂。
Executors.newSingleThreadExecutor:创建一个只有一个线程的线程池,并允许自定义线程工厂。
Executors.newScheduledThreadPool:创建一个用于延迟执行或定期执行任务的线程池,并允许自定义线程工厂。
newCachedThreadPool?
缓存线程池:不固定线程数量,可以根据需要自动创建新线程,适用于短期异步任务。
newFixedThreadPool ?
固定大小线程池:包含固定数量的线程,适用于需要限制并发线程数量的场景。
newScheduledThreadPool ?
定时线程池:可以执行定时任务和周期性任务。
newSingleThreadExecutor ?
单线程池:只包含一个工作线程,保证所有任务按顺序执行,适用于需要保持任务顺序执行的场景。
多线程的优缺点?
优点:提高性能、改善响应性、资源利用率高、并行处理、代码模块化、提高吞吐量
缺点:复杂性增加、竞争条件、死锁、上下文切换开销、资源限制、调试困难、不可预测性、线程安全问题
创建线程的有哪些方式?
继承Thread类、实现Runnable接口、使用Callable和Future创建、使用线程池
谈谈各种创建线程的优缺点?
继承Thread类:
优点:编写简单,容易理解
缺点:由于单继承机制,如果类已经继承另一个类,则无法再继承Thread类
实现Runnable接口:
优点:避免了单继承限制,可实现多个接口。更加灵活,可使用lambda表达式、匿名内部类方式简化代码
缺点:需手动管理线程的生命周期
使用Callable和Future创建:
优点:可获取线程的返回值。可声明抛出异常
缺点:相比Runnable接口更加复杂,需配合FutureTask使用
使用线程池:
优点:提高线程的利用率和系统吞吐量。降低线程创建、销毁的开销。提供更好的线程管理策略,如线程服用、线程缓存等。
确定:需理解线程池的工作原理和配置参数。需注意线程池的资源管理和关闭问题。
对比下你应该选择哪种创建?
需要根据具体的应用场景和需求、编码规范来选择合适的方式。
Runnable和Callable的区别?
核心方法差异:Runnable接口核心方法是run,Callable接口核心方法是call。
使用场景差异:Runnable接口适用简单任务,这些任务不需要返回值,也不会抛出受检异常。Callable接口适用需要返回值的任务或需要抛出受检异常的任务。
异常处理差异:Runnable接口无法对异常进行显式的捕获和处理异常。Callable可以抛出受检异常,可以显式的捕获和处理异常。
与线程类的关系:Runnable接口可以作为Thread类的构造器参数,通过Thread类来启动线程。不能直接作为Thread类的构造器参数,需通过其他方式(FutureTask类)启动线程。
线程的状态流转图?有哪些状态?
线程状态:NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(超时等待)、TERMINATED(终止)
线程池的优点?
资源管理
提高吞吐量
性能提升
减少上下文切换
灵活性
异常处理
任务调度
减少竞争
常用的并发集合类有哪些?
ConcurrentHashMap
ConcurrentLinkedQueue
BlockingQueue接口及其实现
CopyOnWriteArrayList
CopyOnWriteArraySet
ConcurrentSkipListMap
ConcurrentSkipListSet
AtomicReference
Collections.synchronizedMap、Collections.synchronizedList、Collections.synchronizedSet
Executor框架中的线程安全集合
ConcurrentHashMap实现?
分段锁、cas操作和节点级锁定、红黑树优化、弱一致性、容量和负载因子、并发性能
是在并发环境下提供高性能,通过减少锁的粒度来实现。
使用分段锁实现线程安全。使用cas操作来更新结构,减少锁的使用,提高性能。读操作不需要加锁,减少读操作的开销。写操作尝试使用cas操作更新,更新失败,则使用synchronized锁住当前链表或红黑树的节点。遵循内存一致性原则,确保写操作完成后,后续的读操作能看到最新的值。
CopyOnWriteArrayList实现?
主要用于读多写少的场景。
数据结构底层使用数组。读操作不加锁,直接操作数组的快照。写操作会创建数组的新副本,在副本上执行修改操作,然后将原数组引用指向新副本。
CopyOnWriteArraySet实现?
主要用于读多写少的场景。
内部使用一个CopyOnWriteArrayList来存储元素。
谈谈COW?
COW(Copy-On-Write,写时复制)是一种用于优化并发访问的数据结构实现策略。
COW的基本思想是在进行写操作时,不直接修改原数据,而是先复制一份副本,然后在副本上进行修改。写操作完成后,再将副本替换为原数据。
COW实现的并发容器:CopyOnWriteArrayList和CopyOnWriteArraySet。
优势:读操作无需加锁,提高读取效率。避免锁竞争和死锁。适用读多写少的场景。
劣势:写操作内存开销大。极端情况下仍存在数据一致性问题。不适用写操作频繁的场景。
常用的并发工具类有哪些?
Executor框架:Executor接口、Executors工厂类、ExecutorService接口、ScheduledExecutorService接口、ForkJoinPool
同步辅助工具:CountDownLatch、CyclicBarrier、Semaphore(控制同时访问特定资源的线程数量)、Exchanger(用于两个线程交换数据)、Phaser
并发集合:ConcurrentHashMap、ConcurrentLinkedQueue、BlockingQueue接口、CopyOnWriteArrayList、CopyOnWriteArraySet
原子变量类:AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference、AtomicStampedReference
并发工具类:Future、FutureTask、Callable、Runnable、Lock、ReadWriteLock
线程池管理工具:ThreadPoolExecutor
同步器:ReentrantLock、ReentrantReadWriteLock
CyclicBarrier和CountDownLatch的应用场景?
CyclicBarrier:并行任务的等待、一次性事件触发、统计多个线程的执行时间。CyclicBarrier的计数器可以重置,因此可以在所有线程释放后,屏障再次用于下一轮的同步。它适用于需要多次等待多个线程完成任务的场景。
CountDownLatch:并行计算后的汇总、阶段任务同步、游戏或模拟中的回合制同步、批量处理。CountDownLatch的计数器只能减少,且一旦减为0就不能再次使用。它适用于一次性等待多个线程完成任务的场景。
CyclicBarrier和CountDownLatch的区别?
CyclicBarrier 适用于需要多个线程在某个点进行协作的场景,可以重复使用。
CountDownLatch 适用于一个线程等待其他线程完成任务的场景,使用后不能重用。
Semaphore的应用场景?
适合于需要限制资源并发访问数量的场景。
应用场景:控制并发访问、资源池管理、限流、线程执行顺序控制、互斥锁、实现生产者-消费者模式、保护共享资源
synchronized的作用?底层如何实现?
作用:是一种同步机制,用于控制多个线程对共享资源的访问,以确保在任一时刻只有一个线程能够执行特定代码段,从而避免并发问题。
底层实现:synchronized是通过Monitor(监视器)来实现的,Monitor是依赖于底层操作系统的互斥锁实现的。
synchronized和ReentrantLock的区别?
synchronized是java关键字,ReentrantLock是java的类。
synchronized:
锁无需获取和释放
只支持非公平锁
可以修饰普通方法、静态方法、代码块
低并发情况下,性能比ReentrantLock好
底层是JVM层面通过Monitor(监视器)实现
reentrantLock:
锁需手动获取和释放
支持公平锁和非公平锁
只能修饰代码块,但可以对代码块进行精细化的控制
高并发情况下,性能通常比synchronized好
底层基于AQS实现
volatile关键字的作用?底层如何实现?
作用:
用于确保变量的可见性和禁止指令重排。
当一个变量被声明为volatile时,JVM保证了对该变量的读操作总是返回最新的值,即在任意线程中读取该变量时,都能得到该变量的最新值。
写操作时,volatile变量的值会立即被更新到主内存中,且这个更新对所有线程都是可见的。
底层实现:
volatile通过内存屏障实现内存可见性
读操作前,插入Load Barrier,使得屏障之前的所有读/写操作都完成后,才执行该读操作
写操作后,插入Store Barrier,使得屏障之后的写操作都完成后,才执行该操作
什么是CAS?底层如何实现?
CAS是Java并发编程中一种重要的原子操作,用于实现无锁编程。
底层实现:
CAS的底层实现主要依赖于硬件提供的原子性操作指令和JNI(Java Native Interface)技术。
CAS操作通常通过Atomic类实现,类中的CAS方法底层用C语言编写,通过JNI技术调用本地方法实现
CAS有哪些问题?
ABA问题
当一个线程准备用CAS更新一个变量的值时,另一个线程可能已经将该值从A改为了B,然后又改回了A。此时,第一个线程执行CAS操作时会认为值没有发生变化,从而成功更新值。
解决办法:使用版本号、使用带有标记的原子引用
循环时间长开销大
CAS操作是基于自旋锁的一种实现方式。如果CAS操作一直不成功,会导致线程长时间处于忙等(Busy-Wait)状态,从而消耗大量的CPU资源。
只能保证一个共享变量的原子操作
synchronized、volatile、CAS比较?
synchronized:保证代码执行的原子性、可见性和有序性
volatile:用于修饰变量,确保变量的可见性,即一个线程修改了变量的值,新值对其他线程来说是立即可见的。
CAS:通过比较内存中的值和预期值是否相等,来安全地更新变量的值,通常用于实现原子类,如AtomicInteger。
什么是Future?底层如何实现?
Future是Java并发编程中的一个重要接口,它代表了异步计算的结果。
底层实现:
Future是Java并发编程中的一个重要接口,它代表了异步计算的结果。
通过调用Future对象的get()方法来获取任务的结果。
Future对象的状态管理通常依赖于AQS框架。
Future对象会捕获这个异常并保存在内部状态中。
什么是FutureTask?
FutureTask 是 Future 接口的一个实现类,同时也实现了 Runnable 接口,因此可以作为线程执行的任务。
既可以作为 Future 的实现类来获取任务的执行结果,也可以作为线程执行的任务来执行异步操作。
FutureTask 允许在一个单独的线程中执行任务,并且可以获取任务的执行结果。
什么是AQS?底层如何实现?
AQS是Java并发包中的一个核心组件,是构建锁和其他同步器的基础框架。它定义了一套多线程访问共享资源的同步器框架,为Java并发同步组件提供统一的底层支持。
底层实现:内部用一个volatile修饰的int类型的成员变量state来控制同步状态。state = 0表示没有线程正在独占共享资源的锁,state = 1表示有线程正在共享资源的锁
ReadWriteLock读写锁应用场景?
应用场景:缓存场景、配置文件修改、共享文档操作、游戏状态管理、社交软件场景
ReadWriteLock读写锁适用于读多写少的并发场景,通过允许多个线程同时读取共享资源,可以显著提高系统的并发性能。
ReadWriteLock底层实现?
ReentrantReadWriteLock的底层实现是通过AQS来管理锁的状态和同步操作的。
通过将state变量按位拆分来区分读锁和写锁的状态,并提供了读锁和写锁的获取与释放、公平性策略、锁降级等丰富的功能。
ThreadLocal是什么?底层如何实现?
ThreadLocal是Java提供的一个用于创建线程局部变量的机制,它能够为每个使用该变量的线程提供一个独立的变量副本,从而实现了变量的线程隔离。
底层实现:
依赖于Thread类和ThreadLocalMap类。
通过ThreadLocalMap为每个线程提供了独立的变量副本,从而实现了变量的线程隔离。
死锁的常见原因有哪些?
Java死锁的常见原因涉及多个方面,包括竞争系统资源、锁的嵌套与不当的申请顺序、线程间相互等待、锁失效、饥饿与资源分配不均等。
如何避免死锁?有哪些解决方案?
避免同时锁定多个资源
使用定时锁
使用超时锁定
顺序锁定资源
减少锁的粒度
使用并发控制工具
检测死锁
避免嵌套锁定
使用不可变对象
死锁恢复策略
怎么唤醒一个阻塞的线程?
使用wait()和notify()/notifyall()
使用Lock和Condition
改变线程等待的条件
使用中断机制
什么是多线程的上下文切换?
在多个线程之间进行切换的过程
切换原因:
线程阻塞:当一个线程等待I/O操作、获取同步锁或调用某些会挂起线程的方法时,操作系统可能会挂起该线程,并进行上下文切换以运行其他线程。
线程结束:当一个线程执行完毕时,操作系统会进行上下文切换,将CPU控制权交给其他线程。
时间片耗尽:在采用时间片轮转调度的系统中,当一个线程使用完其分配的时间片后,如果它还没有完成执行,操作系统会挂起该线程,并进行上下文切换以运行其他线程。
线程优先级改变:如果一个高优先级的线程变为可运行状态,操作系统可能会挂起当前线程,并进行上下文切换以运行高优先级的线程。
线程调度算法是什么?
是指Java虚拟机(JVM)用来决定哪个线程应该获得CPU资源以执行其任务的规则和策略。
主要调度算法:
时间片轮转调度:系统会为每个线程分配一个固定的时间片,在时间片内,线程可以正常执行。当时间片用完时,如果线程还没有执行完,CPU将把控制权交给下一个线程。
抢占式调度:抢占式调度意味着线程的执行顺序并不是按照先到先得的原则,而是由线程的优先级决定。
协同式调度
多级反馈队列调度
什么是线程调度器和时间分片?
线程调度器:是操作系统或Java虚拟机(JVM)的一部分,负责决定在多线程环境中,哪个线程应该获得CPU时间并执行。
时间分片:时间分片是线程调度中的一个关键概念,它指的是将CPU时间划分成若干个小时间段(即时间片),并轮流分配给每个线程执行。
单例模式的线程安全性?
在多线程环境中,如果多个线程同时访问单例类的实例化代码块,可能会创建多个实例,这违反了单例模式的原则。
单例模式的线程安全实现:
懒汉式:这种方式通过将getInstance方法声明为 synchronized来确保线程安全,但每次调用都会进行同步,性能较低。
双重检查锁定:这种方式只在实例化时进行同步,减少了同步的开销。volatile关键字确保了instance变量的可见性和禁止指令重排。
饿汉式:这种方式在类加载时就完成了实例化,因此是线程安全的,但可能会造成资源的浪费,因为实例可能一直未被使用。
静态内部类:这种方式利用了Java的类加载机制来保证线程安全。SingletonHolder类在第一次被访问时才加载,JVM保证了SingletonHolder类的加载过程是线程安全的。
枚举:枚举类型本身就是线程安全的,并且可以防止反序列化重新创建新的对象。
Executors是什么?
Executors是一个工具类,提供一系列静态工厂方法,用于快速创建和管理线程池。
提供四种线程池类型:固定大小线程池、缓存线程池、单线程池、定时线程池
谈谈ExecutorService,ScheduledExecutorService?
是concurrent包中提供的两个接口,用于异步执行任务。
ExecutorServivice:
ExecutorService是一个执行器服务,允许你异步地执行任务。
特点:任务提交、线程池管理、关闭和终止、返回结果和异常处理。
ScheduledExecutorService:
ScheduledExecutorService是ExecutorService的子接口,它增加了定时任务和周期性任务的执行能力。
特点:定时任务、周期性任务、单次调度和固定频率调度。
登录
查看全部
参与评论
昵称
登录
发布评论
评论留言
还没有评论留言,赶紧来抢楼吧~~
手机查看
返回顶部
给这篇文章打个标签吧~
棒极了
糟糕透顶
好文章
PHP
JAVA
JS
小程序
Python
SEO
MySql
确认