跳到主要内容

高频手撕代码

问题

Java 面试中常考的手撕代码题有哪些?

答案

生产者消费者模型

BlockingQueue 实现
class ProducerConsumer {
private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// 生产者
public void produce() throws InterruptedException {
int item = 0;
while (true) {
queue.put(item); // 队列满时自动阻塞
System.out.println("Produced: " + item++);
}
}

// 消费者
public void consume() throws InterruptedException {
while (true) {
int item = queue.take(); // 队列空时自动阻塞
System.out.println("Consumed: " + item);
}
}
}
wait/notify 实现
class BoundedBuffer {
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity;

BoundedBuffer(int capacity) { this.capacity = capacity; }

public synchronized void put(int item) throws InterruptedException {
while (queue.size() == capacity) wait(); // 必须用 while 防虚假唤醒
queue.offer(item);
notifyAll();
}

public synchronized int take() throws InterruptedException {
while (queue.isEmpty()) wait();
int item = queue.poll();
notifyAll();
return item;
}
}

交替打印(LeetCode 1115)

两个线程交替打印奇偶数
class AlternatePrint {
private volatile int count = 1;
private final Object lock = new Object();

public void printOdd() throws InterruptedException {
synchronized (lock) {
while (count <= 100) {
if (count % 2 == 0) { lock.wait(); continue; }
System.out.println("Odd: " + count++);
lock.notifyAll();
}
}
}

public void printEven() throws InterruptedException {
synchronized (lock) {
while (count <= 100) {
if (count % 2 == 1) { lock.wait(); continue; }
System.out.println("Even: " + count++);
lock.notifyAll();
}
}
}
}

手写线程池(简化版)

简化版线程池核心逻辑
class SimpleThreadPool {
private final BlockingQueue<Runnable> taskQueue;
private final List<Thread> workers;
private volatile boolean running = true;

SimpleThreadPool(int poolSize, int queueSize) {
taskQueue = new ArrayBlockingQueue<>(queueSize);
workers = new ArrayList<>();
for (int i = 0; i < poolSize; i++) {
Thread worker = new Thread(() -> {
while (running || !taskQueue.isEmpty()) {
try {
Runnable task = taskQueue.poll(1, TimeUnit.SECONDS);
if (task != null) task.run();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
worker.start();
workers.add(worker);
}
}

public void submit(Runnable task) throws InterruptedException {
if (running) taskQueue.put(task);
}

public void shutdown() { running = false; }
}

手写单例模式

DCL 双重检查锁定
class Singleton {
private static volatile Singleton instance;
private Singleton() {}

public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile 防止指令重排
}
}
}
return instance;
}
}

手写阻塞队列

ReentrantLock + Condition 实现
class MyBlockingQueue<T> {
private final Queue<T> queue = new LinkedList<>();
private final int capacity;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();

MyBlockingQueue(int capacity) { this.capacity = capacity; }

public void put(T item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) notFull.await();
queue.offer(item);
notEmpty.signal();
} finally {
lock.unlock();
}
}

public T take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) notEmpty.await();
T item = queue.poll();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}

死锁示例与排查

死锁示例
Object lockA = new Object(), lockB = new Object();

// 线程1:先锁A再锁B
new Thread(() -> {
synchronized (lockA) {
try { Thread.sleep(100); } catch (Exception e) {}
synchronized (lockB) { System.out.println("Thread1"); }
}
}).start();

// 线程2:先锁B再锁A → 死锁!
new Thread(() -> {
synchronized (lockB) {
try { Thread.sleep(100); } catch (Exception e) {}
synchronized (lockA) { System.out.println("Thread2"); }
}
}).start();
排查方法

jstack <pid> 或 Arthas thread -b 可以检测死锁。解决:固定加锁顺序或使用 tryLock 超时。


常见面试问题

Q1: wait() 为什么必须在 synchronized 块中?

答案

wait() 会释放当前对象的监视器锁。如果没有持有锁就调用 wait(),会抛 IllegalMonitorStateException

Q2: wait() 为什么要用 while 而不是 if?

答案

防止虚假唤醒(spurious wakeup)。线程被唤醒后条件可能仍不满足(被其他线程抢先处理了),必须重新检查条件。

Q3: Condition 相比 wait/notify 的优势?

答案

一把锁可以创建多个 Condition,精确唤醒特定条件的等待线程。wait/notify 只能随机唤醒一个或全部唤醒。详见 Lock 接口与 AQS

相关链接