高频手撕代码
问题
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。