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
夜雨聆风