乐于分享
好东西不私藏

Java并发容器源码10问

Java并发容器源码10问

Java并发容器源码10问

1. ConcurrentHashMap 1.7 和 1.8 有什么区别

1.7:分段锁(Segment)

staticfinalclass Segment<K,V>extendsReentrantLock{
transientvolatileHashEntry<K,V>[]table;
}

默认16个Segment,每个继承ReentrantLock,并发度固定16。

1.8:CAS + synchronized + 红黑树

finalVputVal(Kkey,Vvalue,booleanonlyIfAbsent){
for(Node<K,V>[]tab=table;;){
if(tab==null)tab=initTable();
elseif((f=tabAt(tab,i=(n-1)&hash))==null){
if(casTabAt(tab,i,null,newNode<>(...)))break;// CAS无锁插入
}elseif(fh==MOVED)tab=helpTransfer(tab,f);// 协助扩容
else{synchronized(f){/* 锁链表头节点 */}}
}
}

变化:粒度从Segment级细化为单bucket级;引入红黑树(≥8转树);多线程协助扩容。

2. ThreadPoolExecutor 核心参数执行流程

publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,
longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,
RejectedExecutionHandlerhandler);

提交流程

线程 < corePoolSize → 新建核心线程
线程 ≥ corePoolSize → 入队workQueue
队列满 & 线程 < maxPoolSize → 新建非核心线程
队列满 & 线程 ≥ maxPoolSize → 拒绝策略

四种策略:AbortPolicy(抛异常)、CallerRunsPolicy(提交线程自己跑)、DiscardPolicy(静默丢弃)、DiscardOldestPolicy(丢弃队首)。

3. ForkJoinPool 工作窃取原理

每个线程维护一个双端队列。自己的线程从队尾取(LIFO),空闲线程偷取队首(FIFO,偷大任务)。

finalvoidpush(ForkJoinTask<?>task){queue[top++]=task;}// 队尾入
finalForkJoinTask<?>pop(){returnqueue[--top];}// 队尾取
finalForkJoinTask<?>poll(){base++;returnqueue[base-1];}// 队首偷

push/pop无锁(仅操作top),poll需CAS竞争base。

4. ArrayBlockingQueue 与 LinkedBlockingQueue 区别

维度 ArrayBlockingQueue LinkedBlockingQueue
单锁(put/take同一把) 双锁(putLock + takeLock)
容量 固定,必须指定 默认Integer.MAX_VALUE
吞吐量 低(锁竞争) 高(生产消费并行)

5. SynchronousQueue 零容量原理

不存储元素,每个put必须等take,反之亦然。newCachedThreadPool 的工作队列即SynchronousQueue。内部有两种模式:TransferQueue(公平FIFO)和TransferStack(非公平LIFO)。

6. DelayQueue 延迟任务实现

优先级队列 + leader-follower模式:多个消费者等队首元素时,只有leader做精确awaitNanos(delay),其他线程无限等待,避免惊群效应。

7. CopyOnWriteArrayList 读写分离

publicEget(intindex){returnelementAt(getArray(),index);}// 无锁读

publicbooleanadd(Ee){
synchronized(lock){// 写加锁
Object[]newArr=Arrays.copyOf(getArray(),len+1);// 复制
newArr[len]=e;
setArray(newArr);// volatile写
}
}

特点:读无锁、写复制整数组。弱一致性(读到过时数据)。适合读多写少。

8. ConcurrentLinkedQueue CAS无锁入队

publicbooleanoffer(Ee){
for(Node<E>t=tail,p=t;;){
Node<E>q=p.next;
if(q==null&&NEXT.compareAndSet(p,null,newNode)){
if(p!=t)TAIL.compareAndSet(t,p);// tail允许滞后
returntrue;
}
}
}

要点:tail允许滞后(减少CAS次数);哨兵节点检测;size()需遍历链表,非O(1)。

9. shutdown() 与 shutdownNow() 区别

shutdown() shutdownNow()
状态 SHUTDOWN STOP
队列任务 继续执行完 返回未执行列表
中断 仅空闲线程 所有线程

最佳实践

pool.shutdown();
if(!pool.awaitTermination(60,TimeUnit.SECONDS))
pool.shutdownNow();

10. 总结速查

容器 核心机制 场景
ConcurrentHashMap CAS+synchronized+红黑树 高并发Map
ThreadPoolExecutor 核心池+队列+拒绝 通用线程池
ForkJoinPool 工作窃取 分治计算
CopyOnWriteArrayList 写时复制 读多写少
DelayQueue 优先队列+leader-follower 延迟任务

参考:OpenJDK 21源码、Java Concurrency in Practice