Files
examination/topics/interview-prep/go-java-concurrency/fill_blank.json
T
wonder 0f68a64829
Deploy Examination / deploy (push) Successful in 10s
feat: add interview-prep topic group with 250 questions (6 subtopics, 5 question types)
Subtopics:
- distributed-microservice: 45 questions (分布式微服务架构)
- message-queue: 45 questions (消息队列)
- k8s-observability: 45 questions (K8s与可观测性)
- go-java-concurrency: 45 questions (Go/Java并发模型)
- database-advanced: 35 questions (数据库进阶)
- ai-engineering: 35 questions (AI工程实践)

Question types: single_choice, true_false, fill_blank, short_answer, code_reading
2026-09-09 16:36:27 +08:00

186 lines
12 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"topic": "go-java-concurrency",
"type": "fill_blank",
"schema_version": "1.0.0",
"generated": "2026-09-09T16:21:56+08:00",
"questions": [
{
"id": "fb-001",
"type": "fill_blank",
"difficulty": 2,
"tags": [
"go",
"gmp"
],
"question": "Go语言GMP调度模型中,P是G和M之间的调度上下文,当一个P的本地队列已满时,新创建的G会被放入______队列,由其他P通过work-stealing机制窃取执行。",
"answer": [
"全局",
"全局队列",
"global queue",
"global"
],
"explanation": "GMP模型中,每个P拥有一个本地可运行G队列(local run queue)。当本地队列容量(默认256)满载时,新创建的G会被放入全局队列(global queue)。调度器会在以下时机从全局队列取出G:1)本地队列为空时;2)每调度61次从全局队列取一个G,防止全局队列中的G被饿死。work-stealing则是当某个P的本地队列为空时,它会尝试从其他P的本地队列窃取一半的G来运行,从而实现负载均衡。",
"source": null,
"related": []
},
{
"id": "fb-002",
"type": "fill_blank",
"difficulty": 2,
"tags": [
"java",
"thread"
],
"question": "Java中创建线程的三种主要方式分别是继承______类、实现Runnable接口和实现Callable接口。",
"answer": [
"Thread"
],
"explanation": "Java提供三种创建线程的方式:1)继承Thread类,重写run()方法,直接调用start()启动;2)实现Runnable接口,将任务与线程解耦,传入Thread构造器;3)实现Callable接口并配合FutureTask,支持返回结果和抛出异常。其中Runnable的run()没有返回值且不能抛出受检异常,而Callable的call()可以返回泛型结果。实际开发中,推荐使用线程池(ExecutorService)而非直接创建线程,以实现资源复用和更好的并发控制。",
"source": null,
"related": []
},
{
"id": "fb-003",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"go",
"channel"
],
"question": "Go的channel底层数据结构hchan中,环形缓冲区______用于存储有缓冲channel中待发送的数据,sendx和recvx分别指向其发送和接收位置。",
"answer": [
"buf",
"ringbuf"
],
"explanation": "hchan是Go channel的底层实现结构,核心字段包括:buf(环形数组缓冲区,仅用于有缓冲channel)、sendx和recvx(分别标记环形缓冲区的发送和接收位置,用于实现FIFO)、sendq和recvq(sudog链表,分别记录因发送/接收阻塞的goroutine)。当channel无缓冲时,buf为nil,发送和接收直接在两个goroutine之间传递数据。环形缓冲区的设计使得sendx到达末尾后可以回绕到数组起始位置,高效利用内存。",
"source": null,
"related": []
},
{
"id": "fb-004",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"java",
"volatile",
"jmm"
],
"question": "Java内存模型中,volatile变量保证可见性的核心原理是:对volatile变量的写操作会立即刷新到______,读操作会从该处重新加载,从而禁止了线程工作内存对该变量的缓存。",
"answer": [
"主内存",
"主存",
"main memory"
],
"explanation": "Java内存模型(JMM)定义了主内存(main memory)和工作内存(working memory)的抽象概念。每个线程拥有自己的工作内存(对应CPU缓存或寄存器),对变量的读写首先在工作内存中进行,再同步到主内存。volatile关键字的关键语义包括:1)保证可见性——写volatile变量时立即刷新到主内存,读时从主内存重新加载;2)禁止指令重排序——通过内存屏障(Memory Barrier)确保volatile写之前的操作不会被重排到写之后,volatile读之后的操作不会被重排到读之前。但注意volatile不能保证原子性,例如i++对volatile变量的自增仍然不是线程安全的。",
"source": null,
"related": []
},
{
"id": "fb-005",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"go",
"sync"
],
"question": "Go的sync.WaitGroup有三个核心方法:Add用于增加计数器,Done用于减少计数器(等同于Add(-1)),当计数器归零时,调用______方法阻塞等待的goroutine会被唤醒。",
"answer": [
"Wait"
],
"explanation": "WaitGroup是Go中用于等待一组goroutine完成的同步原语。典型用法:主goroutine在启动工作goroutine前调用Add(n)设置计数器,每个工作goroutine完成时调用Done()(内部执行Add(-1)),主goroutine调用Wait()阻塞直到计数器归零。底层实现中,WaitGroup使用原子操作维护state1和state2两个字段,分别存储信号量和计数器值。需要注意的是:Add的调用必须在Wait之前或在Wait的goroutine内部调用,不能在工作goroutine中调用Add,否则可能在Wait已经开始等待后才Add,导致竞态条件。",
"source": null,
"related": []
},
{
"id": "fb-006",
"type": "fill_blank",
"difficulty": 3,
"tags": [
"java",
"synchronized",
"cas"
],
"question": "Java synchronized关键字在JDK 6之后引入了锁升级机制,其升级路径为:无锁 → ______ → 轻量级锁(自旋) → 重量级锁,这一优化大幅提升了在不同竞争程度下的同步性能。",
"answer": [
"偏向锁",
"biased locking",
"Biased Locking"
],
"explanation": "JDK 6引入的锁升级(lock escalation)是synchronized性能优化的关键:1)偏向锁——当只有一个线程访问同步块时,在对象头的Mark Word中记录线程ID,后续该线程进入时只需CAS检查ID,无需任何同步操作;2)轻量级锁——当第二个线程尝试获取锁时,偏向锁撤销,升级为轻量级锁,通过CAS将Mark Word复制到栈帧的Lock Record中,适合短时间、低竞争的场景,未获取到锁的线程会通过自旋(adaptive spinning)等待;3)重量级锁——当自旋超过一定次数或竞争激烈时,升级为重量级锁,依赖操作系统的Mutex Lock实现,未获取到锁的线程会被阻塞挂起。锁只能升级不能降级(JDK 15默认关闭了偏向锁)。",
"source": null,
"related": []
},
{
"id": "fb-007",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"go",
"gmp"
],
"question": "GMP调度模型中,当goroutine执行系统调用(如文件I/O)被阻塞时,M会与P解绑,P会被交给其他空闲的M或创建新的M来继续执行本地队列中的G,这一机制被称为______。",
"answer": [
"hand off",
"handoff",
"hand-off"
],
"explanation": "Hand off(移交)是GMP调度模型中处理M阻塞的核心机制。当某个M上的G执行阻塞性系统调用时,M会进入阻塞状态,此时P(及其本地队列中的G)不能跟着一起等待。调度器会将P从这个阻塞的M上解绑(hand off),交给另一个空闲的M来继续执行P本地队列中的G。如果此时没有空闲的M,则会创建一个新M。当系统调用返回后,原M会尝试获取一个空闲的P来继续运行,如果没有空闲P,则该G会被放入全局队列。这一机制确保了一个阻塞的系统调用不会导致整个P上的所有goroutine都被阻塞,提高了调度效率。",
"source": null,
"related": []
},
{
"id": "fb-008",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"java",
"aqs"
],
"question": "Java并发包中的AQS(AbstractQueuedSynchronizer)内部维护了一个volatile int类型的______变量和一个双向链表实现的FIFO队列,ReentrantLock、Semaphore、CountDownLatch等同步器都基于AQS实现。",
"answer": [
"state",
"state变量"
],
"explanation": "AQS是java.util.concurrent包的核心框架,采用模板方法模式,子类通过重写tryAcquire/tryRelease(独占模式)或tryAcquireShared/tryReleaseShared(共享模式)来实现不同的同步语义。核心机制:1)state变量——volatile修饰的整数,不同同步器赋予不同含义:ReentrantLock中表示重入次数(0为未锁定),Semaphore中表示可用许可数,CountDownLatch中表示倒计数值;2)CLH队列——基于双向链表实现的FIFO等待队列,节点封装了等待线程和等待状态;3)acquire/release流程——线程获取锁失败时被封装为节点加入队列尾部并自旋或park阻塞,前驱节点释放后通过unpark唤醒后继节点。",
"source": null,
"related": []
},
{
"id": "fb-009",
"type": "fill_blank",
"difficulty": 4,
"tags": [
"java",
"forkjoinpool"
],
"question": "Java ForkJoinPool采用工作窃取算法,每个工作线程维护一个______队列(deque),当线程自己的任务队列为空时,它会从其他线程队列的尾部窃取任务执行,以此实现负载均衡。",
"answer": [
"双端队列",
"deque",
"Deque",
"双端",
"work-stealing queue"
],
"explanation": "ForkJoinPool是ExecutorService的特殊实现,专为分治任务(divide-and-conquer)设计。其核心设计:1)双端队列(Deque)——每个worker线程拥有自己的双端队列,本地任务从头部push/pop(LIFO,利用缓存局部性),窃取时从其他线程队列的尾部pop(减少竞争);2)工作窃取(work-stealing)——空闲线程从其他忙碌线程的队列尾部窃取任务,避免线程空闲;3)任务拆分——继承RecursiveTask(有返回值)或RecursiveAction(无返回值),通过fork()将子任务推入自己的队列,join()等待子任务结果;4)ForkJoinPool.commonPool()——JDK 8+提供默认共享池,parallelStream()底层使用它。任务粒度过细会导致调度开销过大,过粗则无法充分利用并行性。",
"source": null,
"related": []
},
{
"id": "fb-010",
"type": "fill_blank",
"difficulty": 5,
"tags": [
"go",
"pprof"
],
"question": "Go的pprof工具支持多种profile类型:CPU profile记录程序在各位置的采样频率,heap profile记录内存分配,block profile记录goroutine在同步原语上的阻塞时间,而______ profile专门记录goroutine的调用栈,用于排查goroutine泄漏问题。",
"answer": [
"goroutine",
"goroutine profile"
],
"explanation": "pprof是Go内置的强大性能分析工具,支持的profile类型各有用途:1)CPU profile——通过定时采样(默认100Hz)记录CPU使用热点,用于发现CPU密集型瓶颈;2)heap profile——记录堆内存的分配和持有情况,可用于定位内存泄漏和过度分配;3)block profile——记录goroutine在sync.Mutex、channel等同步原语上阻塞的时长和次数,用于发现锁竞争瓶颈;4)goroutine profile——列出所有活跃goroutine的调用栈和状态(running/waiting/sleeping等),是排查goroutine泄漏的关键工具。当goroutine泄漏时,该profile会显示大量goroutine堆积在同一个调用栈上(如等待永远不会接收到数据的channel)。定位后通常需要检查:未关闭的channel、未取消的context、死循环中缺少退出条件等问题。可通过runtime/pprof包或net/http/pprof端点(/debug/pprof/)获取profile数据,再用go tool pprof进行可视化分析或生成火焰图。",
"source": null,
"related": []
}
]
}