3道京东的Java架构师面试题盘点--ReentrantLock的可重入性原理及实现,线程池的拒绝策略设计与优化,基于Condition实现线程协作(生产者-消费者问题)

题目1:ReentrantLock的可重入性原理及实现

问题背景
在分布式系统中,一个服务可能需要多次调用自身的方法(如嵌套锁场景)。请解释ReentrantLock的可重入性实现原理,并说明其底层如何通过AQS(AbstractQueuedSynchronizer)和CAS操作实现线程安全。

解答

原理分析
  1. 可重入性定义
    可重入锁允许同一个线程多次获取锁而不被阻塞。例如,线程A已持有锁,再次调用lock()时,计数器递增,避免死锁。

  2. AQS核心机制

    • ReentrantLock基于AQS的state字段管理锁状态。
    • state为0表示未锁定,>0表示锁定(值为重入次数)。
    • exclusiveOwnerThread字段记录当前持有锁的线程。
  3. 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实现生产者-消费者问题,并说明awaitsignal的底层机制(如park/unpark)。

解答

原理分析
  1. 核心机制

    • Condition基于AQS的ConditionObject实现,通过awaitsignal协调线程。
    • await:释放锁并挂起当前线程,直到被signal唤醒。
    • signal:唤醒等待队列中的线程,使其重新竞争锁。
  2. 底层实现

    • await:调用LockSupport.park()将线程挂起,并将其加入等待队列。
    • signal:通过LockSupport.unpark()唤醒线程,并将其移动到AQS的同步队列。
  3. 避免虚假唤醒
    必须使用循环条件判断,而非单次判断。

示例代码
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的参数优化任务处理能力。

解答

原理分析
  1. 拒绝策略类型

    • AbortPolicy:直接抛异常(默认)。
    • CallerRunsPolicy:由提交线程执行任务。
    • DiscardPolicy:直接丢弃任务。
    • DiscardOldestPolicy:丢弃队列中最旧的任务。
  2. 自定义策略
    针对突发流量,可设计优先丢弃旧任务并记录日志的策略。

  3. 线程池参数优化

    • 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");
    }
}
优化策略
  1. 动态调整线程池参数
    根据流量监控动态调整maximumPoolSize,避免线程资源耗尽。
  2. 使用优先级队列
    通过PriorityBlockingQueue对任务优先级排序,优先处理紧急任务。
  3. 任务分片与降级
    对非核心任务进行降级处理(如减少计算精度或缓存预热)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图苑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值