Java 并发面试题

2025年3月17日 | 阅读 8 分钟

Java 并发是面试中一个重要的考点,它在加快 Java 面试通过率方面发挥着重要作用。Java 并发通过将任务分解为子任务,然后同步执行这些子任务,从而确保任务执行得更快。

以下是一些重要的 Java 并发问题,可以帮助候选人通过 Java 面试。

1) 解释 Java 并发中的 CountDownLatch。

CountDownLatch 是 Java 中的一个同步器,它允许一个线程在开始处理之前等待一个或多个线程。它在服务器端 Java 应用程序中起着重要作用。

如果其他线程中有一个或多个事件被使用,而我们想在当前执行的线程中使用这些事件,我们可以使用 CountDownLatch 将该线程置于等待状态,直到使用这些事件的线程完成。


2) 解释 Java 并发中的 CyclicBarrier。

与 CountDownLatch 类似,CyclicBarrier 的概念也用于将线程置于等待状态。CyclicBarrier 使线程互相等待,直到达到共同的屏障点。当每个线程到达屏障时,我们需要在 CyclicBarrier 对象上调用 await() 方法。


3) 假设一个 CountDownLatch 初始化为 3,即 new CountDownLatch(3)。是否需要三个线程来进行倒计时?

不一定需要三个线程,或者说,倒计时需要的线程数量没有限制。


4) 解释 Java 并发中的 Phaser。

Phaser 在一个或多个活动阶段的线程同步中起着重要作用。当 Phaser 用于同步单个阶段时,它的作用类似于 CyclicBarrier。它非常灵活且可重用。


5) 解释 Java 并发中的 Exchanger。

顾名思义,它与交换有关。Exchanger 在两个线程之间交换数据方面起着重要作用。Exchanger 使线程之间轻松共享数据。Exchanger 提供了一个同步点,线程可以在此处配对并交换元素。


6) 解释 Java 并发中的 Semaphore。

Semaphore 是 `java.util.concurrent` 包中的一个类。它基本上是一个维护一组可用许可的信号量。

线程使用 `acquire()` 方法获取许可以访问共享资源。如果 Semaphore 的计数不为零,则 Semaphore 的计数将减一,并获取一个许可。否则,线程将被阻塞,直到有可用许可。线程在完成对共享资源的使用后,使用 `release()` 方法释放它们。


7) 解释 Java 并发中的 ReentrantLock。

ReentrantLock 是一个实现 Lock 接口的类。当线程访问共享资源时,它为方法提供同步。ReentrantLock 允许线程多次进入锁。


8) 解释 Java 并发中的 ReadWriteLock。

ReadWriteLock 在 Java 多线程应用程序中起着重要作用。在多线程应用程序中,多个读和写可以同时发生于共享资源。它主要用于两个多个写发生或同时发生“读写”的情况。在读写的情况下,可能会读到错误的值。因此,ReadWriteLock 主要用于通过锁定读取或写入操作来提高性能。


9) 解释 Java 并发中的 ReentrantReadWriteLock。

这是一个实现 ReadWriteLock 接口的接口类,它提供了一对读写锁。我们分别使用 `readWrite.readLock.lock()` 和 `readWrite.writeLock.lock()` 方法来获取读锁和写锁。这里,`readWrite` 是 ReentrantReadWriteLock 类的实例。ReentrantReadWriteLock 还允许从写锁降级为读锁。


10) 解释 Java 并发中的 ConcurrentHashMap。

ConcurrentHashMap 类似于 HashMap。HashMap 和 ConcurrentHashMap 之间的唯一区别在于 ConcurrentHashMap 使用的锁定策略。ConcurrentHashMap 不会对每个方法进行公共锁同步。


11) 解释 Java 并发中的 Lock Striping。

Lock Striping 概念用于将锁分离到数据结构的一部分,其中每个锁都锁定一组可变大小的独立对象。


12) 解释 Java 并发中的 CopyOnWriteArrayList。

CopyOnWriteArrayList 是一个实现 List 接口的类。List 和 CopyOnWriteArrayList 的主要区别在于 CopyOnWriteArrayList 是线程安全的,而 List 不是。在迭代次数多于修改次数的情况下,它提供了更好的性能。


13) 解释 Java 并发中 CopyOnWriteArrayList 和 ArrayList 的区别。

两者之间的主要区别在于 ArrayList 不是线程安全的,并且不适合在多线程环境中使用。CopyOnWriteArrayList 适用于多线程环境,因为它线程安全。ArrayList 和 CopyOnWriteArrayList 返回的迭代器分别是快速失败 (fail-fast) 和安全复制 (fail-safe)。


14) 解释 Java 并发中的 CopyOnWriteArraySet。

另一个线程安全的集合,它利用 CopyOnWriteArrayList 来执行所有操作,称为 CopyOnWriteArraySet。CopyOnWriteArraySet 的线程安全性与 CopyOnWriteArrayList 相同,因为 CopyOnWriteArraySet 在内部使用了 CopyOnWriteArrayList。


15) 解释 Java 并发中的 ConcurrentSkipListMap。

ConcurrentSkipListMap 是一个实现 ConcurrentNavigableMap 的类。ConcurrentNavigableMap 类似于 TreeMap,并增加了并发功能。它根据键的自然顺序对元素进行排序。


16) 解释 Java 并发中的 Callable。

在 Java 中,Callable 是 Java5 或更高版本中提供的一个接口。它主要用于通过线程异步执行任务。它提供了一个 `Call()` 方法,该方法可以返回任何类型的值,因为 Callable 接口是一个泛型接口。


17) 解释 Java 并发中的 Future。

在 Java 中,Future 也是一个接口,主要用于表示异步任务的结果。Future 对象由 ExecutorService 的 `submit()` 方法返回。Future 接口提供了几种方法来检查计算是否完成、等待其完成以及检索计算结果。


18) 解释 Java 并发中的 ThreadPool。

ThreadPool 在 Java 并发中起着非常重要的作用。它基本上是一组等待工作的线程,可以被再次使用。在 ThreadPool 中,会创建一个线程集合(固定大小),然后从中拉出一个线程,服务提供者将一个作业分配给该线程。


19) 解释 Java 并发中的 ExecutorService。

ExecutorService 接口支持 Executor 接口,并通过提供多种方法来管理终止和返回 Future 对象的多个异步任务的进度跟踪。

ExecutorService 接口的 `submit` 方法比其他方法更通用。`submit` 不仅接受 Runnable 对象,还接受 Callable 对象。


20) 列出实现 Executor 或 ExecutorService 接口的类。

在 Java 中,有几个类实现了 Executor 或 ExecutorService 接口。其中一些如下:

  1. ThreadPoolExecutor 类通过使用池中的一个线程来支持这两个接口以执行已提交的任务。
  2. ForkJoinPool 支持这两个接口。此类主要由 Fork/Join 框架使用。
  3. ScheduledThreadPoolExecutor 支持 ThreadPoolExecutor 类和 ScheduledExecutorServiceScheduledThreadPoolExecutor 类主要用于调度命令在给定延迟后运行或定期执行。

21) 解释 `submit()` 和 `execute()` 方法的区别。

Executor 接口提供 `execute()` 方法。`execute()` 方法不返回任何内容,并接受 Runnable 命令作为参数。

ExecutorService 接口提供 `submit()` 方法,该方法比 `execute()` 方法更通用。`submit()` 方法不仅接受 Runnable 接口,还接受 Callable 对象。与 `execute()` 方法不同,`submit()` 方法返回 Future 类型的返回值或对象。


22) 解释 ScheduledExecutorService。

ScheduledExecutorService 接口在 Java 并发中起着非常重要的作用。该接口支持 Executor 接口,并提供用于调度命令定期执行或在给定时间后运行的方法。

它提供了以下两种方法,用于在指定时间段后执行 Callable 和 Runnable 任务:

  1. schedule(Callable<V> callable, long delay, TimeUnit unit)
  2. schedule(Runnable command, long delay, TimeUnit unit)

23) 解释 Java 并发中的 Executors 类。

Executors 类提供了 Executor 框架的类(如 ExecutorServiceExecutorCallableScheduledExecutorService 等)的各种方法。我们可以直接使用 ThreadPoolExecutorScheduledThreadPoolExecutor,但这并不是最好的方法。获取执行器(executor)的最佳方法是使用实用类的静态工厂方法。以下是一些静态工厂方法:

1. newCachedThreadPool() 方法

此方法主要用于创建线程池,这些线程池使用先前创建的线程或按需创建新线程。

2. newFixedThreadPool(int numThreads)

此方法也用于创建使用固定数量的先前创建线程的线程池。

3. newScheduledThreadPool(int numThreads)

此方法也用于创建用于调度命令定期执行或在指定时间后运行的线程池。

4. newSingleThreadExecutor()

此方法主要用于创建 Executor。创建的执行器使用单个工作线程处理无界队列。


24) 解释 Java 并发中的 ConcurrentLinkedQueue。

ConcurrentLinkedQueue 是一个无界且线程安全的队列。它以链表节点的形式存储元素。它遵循 FIFO(先进先出)原则。


25) 解释 Java 并发中的 ConcurrentLinkedDeque。

与 ConcurrentLinkedQueue 类似,ConcurrentLinkedDeque 也是一个无界线程安全的双端队列。它支持 Deque 接口,因此可以从两端进行插入和删除。


26) 解释 Java 并发中的 BlockingQueue。

BlockingQueue 接口在 Java 5 或更高版本中可用。BlockingQueue 是一个主要用于阻塞操作的队列。BlockingQueue 只支持那些等待队列的操作。

BlockingQueue 具有以下两种用于阻塞操作的方法:

  1. `put(element)` 方法用于将指定元素插入队列。
  2. `take()` 方法用于从队列的头部检索并移除元素。

27) Java 并发中的阻塞方法是什么?

阻塞方法是指在不将控制权移交给其他线程的情况下执行分配任务的方法。


28) 解释 Java 并发中的 LinkedBlockingQueue。

LinkedBlockingQueue 支持 BlockingQueue 接口,并在内部使用链表节点来存储元素。与 ArrayBlockingQueue 不同,LinkedBlockingQueue 是可选绑定的。


29) 解释 Java 并发中的 PriorityBlockingQueue。

PriorityBlockingQueue 也支持 BlockingQueue 接口,并以它们自然排序的方式存储元素。元素的顺序还基于在队列构造时提供的比较器。


30) 解释 Java 并发中的 SynchronousQueue 和 DelayQueue。

SynchronousQueue 也支持 BlockingQueue,并且没有内部容量。在此队列中,每个“插入”操作都必须等待另一个线程执行相应的“移除”操作,反之亦然。

DelayQueue 也支持 BlockingQueue,并且是一个无界队列接口。我们只能存储延迟类型的元素,并且只能在延迟到期后检索它们。