Java 多线程中级面试题

1. 什么是线程死锁?如何避免?

死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种僵局。避免死锁的方法包括打破循环等待条件、使用顺序锁、使用超时机制等。

2. Java中`synchronized`和`ReentrantLock`有什么区别?

  • synchronized是内置的同步机制,而ReentrantLock是Java并发包中的显式锁。
  • ReentrantLock提供了更多的功能,如尝试非阻塞获取锁、可中断的锁获取等。
// synchronized
public synchronized void method() {
    // 方法体
}

// ReentrantLock
Lock lock = new ReentrantLock();
lock.lock();
try {
    // 方法体
} finally {
    lock.unlock();
}

3. `volatile`关键字有什么作用?

volatile关键字确保变量的读写操作对所有线程都是可见的,并且禁止指令重排。

volatile boolean running = true;

4. `ThreadLocal`变量是什么?它有什么作用?

ThreadLocal变量是线程局部变量,每个线程都有自己独立的变量副本,用于隔离线程环境。

ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();
threadLocalValue.set(123);

5. `wait()`和`notify()`方法的作用是什么?

wait()使当前线程等待,直到其他线程调用相同对象的notify()notifyAll()方法。

synchronized (object) {
    while (condition) {
        object.wait();
    }
    // 线程被唤醒后执行的代码
}

6. `sleep()`和`wait()`有什么区别?

  • sleep()Thread类的方法,它使当前线程暂停执行指定的时间,但不释放对象锁。
  • wait()Object类的方法,它使当前线程等待,直到被notify()notifyAll()唤醒,并且释放对象锁。

7. `join()`方法的作用是什么?

join()方法等待调用该方法的线程完成执行。

Thread thread = new Thread(() -> {
    // 线程执行的操作
});
thread.start();
thread.join(); // 等待线程结束

8. `Callable`和`Runnable`有什么区别?

Callable可以返回值和抛出异常,而Runnable不能。

Callable<Integer> callable = () -> {
    return 123;
};

Runnable runnable = () -> {
    // 无返回值和异常抛出
};

9. `Future`和`FutureTask`有什么区别?

FutureFutureTask的接口,FutureTaskFuture的实现类,用于处理异步计算的结果。

FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
Integer result = futureTask.get(); // 获取计算结果

10. `ExecutorService`的作用是什么?

ExecutorService是一个执行提交的 Runnable 任务的线程池。

ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(callable);
executor.shutdown();

11. `ConcurrentHashMap`是如何保证线程安全的?

ConcurrentHashMap通过分段锁机制来保证线程安全。

12. `CopyOnWriteArrayList`是如何工作的?

CopyOnWriteArrayList在修改操作时复制整个底层数组,适用于读多写少的场景。

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("Element");

13. `ReadWriteLock`的作用是什么?

ReadWriteLock允许多个读操作同时进行,但写操作是独占的。

ReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

readLock.lock();
try {
    // 读操作
} finally {
    readLock.unlock();
}

writeLock.lock();
try {
    // 写操作
} finally {
    writeLock.unlock();
}

14. `AtomicInteger`和`synchronized`有什么区别?

AtomicInteger利用CAS操作实现无锁的原子操作,而synchronized是锁机制。

AtomicInteger atomicInteger = new AtomicInteger(0);
atomicInteger.incrementAndGet();

15. `CountDownLatch`和`CyclicBarrier`有什么区别?

CountDownLatch用于等待一组事件发生,而CyclicBarrier用于等待线程达到一个共同点,然后继续执行。

CountDownLatch latch = new CountDownLatch(1);
latch.await(); // 等待计数器到达0

CyclicBarrier barrier = new CyclicBarrier(2);
barrier.await(); // 等待其他线程到达屏障

16. `Semaphore`的作用是什么?

Semaphore用于控制对资源的访问,它是一个计数信号量。

Semaphore semaphore = new Semaphore(1);
semaphore.acquire(); // 请求一个许可
try {
    // 访问资源
} finally {
    semaphore.release(); // 释放许可
}

17. `Exchanger`的作用是什么?

Exchanger用于在两个线程之间交换数据。

Exchanger<String> exchanger = new Exchanger<>();
Thread thread = new Thread(() -> {
    String data = exchanger.exchange("Data");
});
thread.start();
String exchangedData = exchanger.exchange("Data to exchange");

18. `Fork/Join`框架的作用是什么?

Fork/Join框架是一种用于并行计算的框架,它通过分而治之的方式将任务分解成更小的任务并行执行。

ForkJoinPool pool = new ForkJoinPool();
RecursiveTask<Integer> task = new RecursiveTask<Integer>() {
    // 实现compute方法
};
pool.invoke(task);

19. `Phaser`和`Barrier`有什么区别?

PhaserBarrier的扩展,它允许多个线程在多个阶段同步。

20. `CompletableFuture`的作用是什么?

CompletableFuture用于编写异步代码,它提供了丰富的API来处理异步计算的结果。

CompletableFuture.supplyAsync(() -> {
    return "Result";
}).thenAccept(result -> {
    System.out.println(result);
});

21. `ThreadFactory`的作用是什么?

ThreadFactory用于创建线程,它允许自定义线程的创建过程。

ThreadFactory factory = Executors.defaultThreadFactory();
Thread thread = factory.newThread(runnable);

22. `Executor`和`Executors`有什么区别?

Executor是执行任务的接口,而Executors是提供了一系列工厂方法来创建预定义配置的Executor实现。

23. `BlockingQueue`的作用是什么?

BlockingQueue是一个线程安全的队列,支持阻塞操作。

BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
queue.put(1); // 阻塞直到队列可用
Integer take = queue.take(); // 阻塞直到队列中有元素

24. `ArrayBlockingQueue`和`LinkedBlockingQueue`有什么区别?

ArrayBlockingQueue是基于数组实现的有界队列,而LinkedBlockingQueue是基于链表实现的可选边界队列。

25. `PriorityBlockingQueue`的作用是什么?

PriorityBlockingQueue是一个线程安全的优先级队列。

PriorityBlockingQueue<Integer> queue = new PriorityBlockingQueue<>(11);
queue.put(5);
Integer take = queue.take(); // 按照优先级(自然顺序)取出元素

26. `DelayQueue`的作用是什么?

DelayQueue是一个线程安全的无界队列,支持延迟操作。

DelayQueue<Delayed> queue = new DelayQueue<>();
queue.put(new Delayed(1, TimeUnit.SECONDS));
Delayed take = queue.take(); // 阻塞直到延迟到期

27. `SynchronousQueue`的作用是什么?

SynchronousQueue是一个不存储元素的阻塞队列,每个插入操作必须等待一个移除操作。

SynchronousQueue<Runnable> queue = new SynchronousQueue<>();
queue.put(() -> {}); // 阻塞直到另一个线程移除元素
Runnable take = queue.take(); // 阻塞直到另一个线程插入元素

28. `ThreadFactoryBuilder`如何使用?

ThreadFactoryBuilder用于构建自定义的ThreadFactory

ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
    .setNameFormat("my-thread-%d").build();

29. `Executors`类提供的线程池有什么特点?

Executors类提供了固定大小、可缓存、单线程和定时/周期性任务的线程池实现。

30. `ScheduledExecutorService`的作用是什么?

ScheduledExecutorService用于延迟执行或定期执行任务。

ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
scheduledExecutor.scheduleAtFixedRate(() -> {
    // 定期执行的任务
}, 0, 1, TimeUnit.SECONDS);