题目1:ReentrantLock的可重入性原理及实现
问题背景:
在分布式系统中,一个服务可能需要多次调用自身的方法(如嵌套锁场景)。请解释ReentrantLock
的可重入性实现原理,并说明其底层如何通过AQS(AbstractQueuedSynchronizer)和CAS操作实现线程安全。
解答:
原理分析
-
可重入性定义:
可重入锁允许同一个线程多次获取锁而不被阻塞。例如,线程A已持有锁,再次调用lock()
时,计数器递增,避免死锁。 -
AQS核心机制:
ReentrantLock
基于AQS的state
字段管理锁状态。state
为0表示未锁定,>0表示锁定(值为重入次数)。exclusiveOwnerThread
字段记录当前持有锁的线程。
-
CAS操作实现:
- 加锁流程:
当线程尝试获取锁时,通过CAS
(Compare and Swap)尝试将state
从0改为1。
如果当前线程已持有锁(exclusiveOwnerThread == currentThread
),则直接增加state
的计数(无需CAS)。 - 解锁流程:
每次调用unlock()
时,state
递减。当state
为0时,释放锁并唤醒等待队列中的线程。
- 加锁流程:
示例代码
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
public void outerMethod() {
lock.lock();
try {
System.out.println("Outer method acquired lock");
innerMethod();
} finally {
lock.unlock();
}
}
private void innerMethod() {
lock.lock(); // 可重入,无需阻塞
try {
System.out.println("Inner method re-acquired lock");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
ReentrantLockDemo demo = new ReentrantLockDemo();
demo.outerMethod(); // 输出两条日志,无死锁
}
}
底层实现关键代码(AQS简化版)
// AQS中的tryAcquire方法(ReentrantLock的实现)
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
Thread current = Thread.currentThread();
// 获取当前state值
int c = getState();
// 如果锁未被其他线程持有(state为0),尝试CAS设置为1
if (c == 0) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程已经持有锁,递增state
else if (current == getExclusiveOwnerThread()) {
setState(c + acquires); // 直接更新,无需CAS
return true;
}
// 否则竞争失败
return false;
}
题目2:基于Condition实现线程协作(生产者-消费者问题)
问题背景:
在消息队列系统中,生产者和消费者线程需要协作,避免资源竞争。请用Condition
实现生产者-消费者问题,并说明await
和signal
的底层机制(如park/unpark
)。
解答:
原理分析
-
核心机制:
Condition
基于AQS的ConditionObject
实现,通过await
和signal
协调线程。await
:释放锁并挂起当前线程,直到被signal
唤醒。signal
:唤醒等待队列中的线程,使其重新竞争锁。
-
底层实现:
await
:调用LockSupport.park()
将线程挂起,并将其加入等待队列。signal
:通过LockSupport.unpark()
唤醒线程,并将其移动到AQS的同步队列。
-
避免虚假唤醒:
必须使用循环条件判断,而非单次判断。
示例代码
public class BoundedBuffer {
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final Object[] items = new Object[10];
private int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) // 循环判断
notFull.await(); // 释放锁并挂起
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal(); // 唤醒消费者
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0) // 循环判断
notEmpty.await(); // 释放锁并挂起
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal(); // 唤醒生产者
return x;
} finally {
lock.unlock();
}
}
}
底层关键代码(ConditionObject的await)
// ConditionObject的await方法
public final void await() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
Node node = addConditionWaiter(); // 将节点加入等待队列
int savedState = fullyRelease(node); // 释放锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) // 确保节点移到同步队列
LockSupport.park(this); // 挂起线程
// 后续竞争锁...
}
题目3:线程池的拒绝策略设计与优化
问题背景:
在高并发的电商系统中,线程池可能因任务积压而拒绝新任务。请设计一个自定义的拒绝策略,并说明如何通过ThreadPoolExecutor
的参数优化任务处理能力。
解答:
原理分析
-
拒绝策略类型:
AbortPolicy
:直接抛异常(默认)。CallerRunsPolicy
:由提交线程执行任务。DiscardPolicy
:直接丢弃任务。DiscardOldestPolicy
:丢弃队列中最旧的任务。
-
自定义策略:
针对突发流量,可设计优先丢弃旧任务并记录日志的策略。 -
线程池参数优化:
corePoolSize
:核心线程数,长期存活。maximumPoolSize
:最大线程数,应对突发流量。keepAliveTime
:非核心线程空闲时长后回收。workQueue
:阻塞队列(如ArrayBlockingQueue
)容量需合理设置。
示例代码
public class CustomThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 20, // corePoolSize和maximumPoolSize
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new ThreadPoolExecutor.CallerRunsPolicy() // 自定义策略
);
// 自定义拒绝策略
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
System.out.println("Task discarded due to pool overload");
// 丢弃最旧任务或记录日志
}
}
});
// 提交任务
for (int i = 0; i < 2000; i++) {
executor.execute(new Task());
}
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println("Processing task");
}
}
优化策略
- 动态调整线程池参数:
根据流量监控动态调整maximumPoolSize
,避免线程资源耗尽。 - 使用优先级队列:
通过PriorityBlockingQueue
对任务优先级排序,优先处理紧急任务。 - 任务分片与降级:
对非核心任务进行降级处理(如减少计算精度或缓存预热)。