面试题-并发篇

张开发
2026/4/4 10:06:44 15 分钟阅读
面试题-并发篇
Java中的线程状态,之间是如何转变的java线程分为六种状态创建线程对象是NEW调用了start()方法转变为RUNNABLE线程获取到了CPU的执行权执行结束是TERMINATED[ˈtɜːrməˌnet/]在可执行状态的过程中如果没有获取CPU的执行权可能会切换其他状态如果没有获取锁synchronized或lock进入BLOCKED状态获得锁再切换为RUNNABLE如果线程调用了wait()方法进入WAITING其他线程调用notify()唤醒后可切换为RUNNABLE如果线程调用了sleep(50)或带指定等待时间的wait()进入TIMED_WAITING到时间后可切换为Runnable进程和线程的区别进程是正在运行程序的实例进程中包含了线程每个线程执行不同的任务不同的进程使用不同的内存空间在当前进程下的所有线程可以共享内存空间并行和并发有什么区别并发是多个任务在同一时间段内交替执行宏观同时微观串行并行是多个任务在同一时刻同时执行创建线程的方式有哪些?继承Thread类重写run方法实现runnable接口,实现run方法实现Callable接口(需要使用FutureTask封装) 实现call方法线程池创建线程(项目中使用方式)java中单继承多实现Runnable和Callable有什么区别Runnable 接口run方法没有返回值Callable接口call方法有返回值和FutureTask配合可以用来获取异步执行的结果Callable接口的call()方法允许抛出异常而Runnable接口的run()方法的异常只能在内部消化不能继续上抛线程的 run()和 start()有什么区别start() 用于启动一个新线程JVM 会调用该线程的 run() 方法run() 只是普通的方法调用不会启动新线程会在当前线程中顺序执行。如何保证多个线程按顺序执行可以使用线程中的join()方法解决阻塞调用此方法的线程进入timed_waiting直到等待的线程执行完成后此线程再继续执行notify()和 notifyAll()有什么区别notifyAll唤醒所有wait的线程notify只随机唤醒一个 wait 线程在java中wait和sleep方法的不同wait() 是 Object 类的方法sleep() 是 Thread 类的静态方法wait() 会释放锁sleep() 不会释放锁wait() 需要 notify() / notifyAll() 唤醒sleep() 时间到自动唤醒wait() 必须在同步代码块中使用sleep() 可以在任何地方使用如何停止一个正在运行的线程Java 中推荐使用中断机制来停止线程通过 interrupt() 设置中断标志线程内部配合 isInterrupted() 或 InterruptedException 响应中断并自动退出而不是强制停止。stop() 方法已被废弃不安全synchronized关键字的底层原理Synchronized【对象锁】采用互斥的方式让同一时刻至多只有一个线程能持有锁它的底层由monitor实现,线程获得锁需要使用对象锁关联monitor在monitor内部有三个属性分别是owner、entrylist、waitset其中owner是关联的获得锁的线程并且只能关联一个线程entrylist关联的是处于阻塞状态的线程waitset关联的是处于Waiting状态的线程Monitor实现的锁属于重量级锁你了解过锁升级吗synchronized 有偏向锁、轻量级锁、重量级锁三种形式对应三种场景偏向锁长时间只有一个线程使用第一次 CAS 记录线程 ID后续只判断 ID无 CAS 开销。轻量级锁不同线程交替持有锁通过 CAS 修改对象头的锁标志无阻塞。重量级锁多线程竞争锁底层使用 Monitor涉及用户态和内核态切换开销大。一旦发生竞争都会升级为重量级锁你谈谈 JMMJava内存模型JMM(Java Memory Model)Java内存模型定义了多线程程序中共享变量的访问规则确保指令的正确性。JMM抽象了主内存和工作内存的概念:每个线程都有自己的工作内存所有线程共享主内存线程之间交互必须通过主内存。CAS 知道吗CAS全称compare and swap比较再交换它体现的是乐观锁的思想保证在无阻塞状态下操作数据的原子性底层调用的是unsafe类中的方法最终是通过cpu的原子指令实现的。cas操作中包含三个参数分别是内存地址V,期望值E新值N。只有当内存地址V等于期望值E的时候才会将V改为N否则就会进行自旋重试。相比传统的synchronized 锁CAS在低竞争场景下性能更好因为它避免了线程阻塞和上下文切换的开销。但在高竞争场景下自旋重试可能浪费CPU资源。CAS使用到的地方很多AQS(AbstractQueuedSynchronizer)框架、AtomicXXX类(AtomicInteger,AtomicBoolean,AtomicLong,AtomicReference)乐观锁和悲观锁的区别CAS 是基于乐观锁的思想最乐观的估计不怕别的线程来修改共享变量就算改了也没关系再重试就可以了synchronized 是基于悲观锁的思想最悲观的估计得防着其它线程来修改共享变量只有修改完了解开锁其他线程才有机会请谈谈你对 volatile 的理解保证线程间的可见性:用 volatile 修饰共享变量能够防止编译器等优化发生让一个线程对共享变量的修改立即刷新到主内存其他线程读取最新值禁止指令重排序:用 volatile 修饰共享变量会在读、写共享变量时加入不同的屏障阻止其他读写操作越过屏障从而达到阻止重排序的效果什么是AQS(AbstractQueuedSynchronizer)AQS 是多线程中的队列同步器是 ReentrantLock、Semaphore 等锁机制的基础框架。内部维护一个 FIFO 双向队列存储等待的线程还有一个 volatile int state 表示资源状态默认 0无锁。线程通过 CAS 将 state 从 0 改为 1 即获得锁失败则入队等待ReentrantLock的实现原理ReentrantLock 基于 AQS 实现核心是 volatile int state 状态变量和 FIFO 等待队列。加锁时通过 CAS 将 state 从 0 改为 1成功则获取锁失败则进入队列等待。重入时 state 递增。解锁时 state 递减当state为0则释放锁并唤醒队列中等待的线程。支持公平和非公平两种模式,默认为非公平锁。公平锁则体现在按照先后顺序获取锁非公平体现在不在排队的线程也可以抢锁synchronized和Lock有什么区别 ?语法层面synchronized 是关键字自动释放锁Lock 是接口需手动 调用unlock()释放锁功能层面都是悲观锁支持互斥和锁重入。但 Lock 额外支持公平锁、可打断、可超时、多条件变量性能层面低竞争时 synchronized 有偏向锁、轻量级锁优化高竞争时 Lock 性能更好死锁产生的条件是什么如何避免当多个线程互相持有对方所需要的资源就会导致线程等待容易发生死锁尽量避免使用多个锁减少锁的粒度使用尝试锁避免嵌套锁聊一下ConcurrentHashMap采用的数据结构跟HashMap1.8的结构一样数组链表/红黑二叉树用CAS添加新节点采用synchronized锁定链表或红黑树的首节点,性能更好导致并发程序出现问题的根本原因是什么(Java程序中怎么保证多线程的执行安全)原子性 synchronized、lock可见性 volatile、synchronized有序性 volatile为什么需要线程池线程池通过复用线程避免了频繁创建和销毁线程的开销从而提升系统性能。同时它能有效控制并发线程数量防止因资源耗尽导致系统崩溃。说一下线程池的核心参数(7)corePoolSize 核心线程数量maximumPoolSize 最大线程数量 (核心线程救急线程的最大数目)keepAliveTime 救急线程的空闲存活时间unit 生存时间单位如秒、毫秒等workQueue - 阻塞队列当没有空闲核心线程时新来任务会加入到此队列排队队列满会创建救急线程执行任务threadFactory 线程工厂 - 可以定制线程对象的创建例如设置线程名字、是否是守护线程等handler 拒绝策略 - 当所有线程都在繁忙workQueue 也放满时会触发拒绝策略线程池中有哪些常见的阻塞队列LinkedBlockingQueue默认无界支持有界底层是链表是懒惰的创建节点的时候添加数据入队会生成新 Node两把锁头尾ArrayBlockingQueue强制有界底层是数组提前初始化 Node 数组Node需要是提前创建好的一把锁线程池的种类有哪些newFixedThreadPool创建一个定长线程池可控制线程最大并发数超出的线程会在队列中等待newSingleThreadExecutor创建一个单线程化的线程池它只会用唯一的工作线程来执行任 务保证所有任务按照指定顺序(FIFO)执行newCachedThreadPool创建一个可缓存线程池如果线程池长度超过处理需要newScheduledThreadPool可以执行延迟任务的线程池支持定时及周期性任务执行阿里巴巴不让使用JDK自带的线程池Executors里面默认提供的几个线程池是有一些弊端的如果盲目去使用可能会造成比较严重的事故。FixedThreadPool和SingleThreadExecutor中阻塞队列长度的Integer的最大值一旦请求量增加就会导致大量请求阻塞在队列中可能会造成内存溢出的问题。CachedThreadPool和ScheduledThreadPool中最大线程数是Integer的最大值一旦请求量增加就会导致创建大量的线程使处理性能降低甚至会出现宕机的问题。如何控制某个方法允许并发访问线程的数量Semaphore [ˈseməfɔː®] 信号量在多线程中提供了一个工具类Semaphore信号量。在并发的情况下可以控制方法的访问量创建Semaphore对象可以给一个容量acquire()可以请求一个信号量这时候的信号量个数-1release()释放一个信号量此时信号量个数1谈谈你对ThreadLocal的理解ThreadLocal 可以实现【资源对象】的线程隔离让每个线程各用各的【资源对象】避免争用引发的线程安全问题每个线程内有一个 ThreadLocalMap 类型的成员变量来存储键值对其中键是 ThreadLocal 对象本身值是变量的副本。ThreadLocal内存泄漏问题ThreadLocalMap 中的 key 是弱引用值为强引用 key 会被GC 释放内存关联 value 的内存并不会释放。建议主动 remove 释放 keyvalue

更多文章