JavaRush /Java 博客 /Random-ZH /翻译:按主题排列的前 50 个面试问题。第2部分。
KapChook
第 19 级
Volga

翻译:按主题排列的前 50 个面试问题。第2部分。

已在 Random-ZH 群组中发布
原文第二部分翻译为新手、经验丰富的程序员的 Top 50 Java 线程面试问题答案。 第一部分。
  1. 如何检查线程是否持有锁?

  2. 我不知道你可以检查线程当前是否持有锁,直到我在电话采访中遇到这个问题。java.lang.Thread有一个holdsLock()方法,当且仅当当前线程持有特定对象的监视器时,它返回true。
  3. 如何获取线程转储?

  4. 线程转储允许您找出线程当前正在做什么。有多种方法可以获取线程转储,具体取决于操作系统。在 Windows 上,您可以使用 ctrl + Break 组合,在 Linux 上,您可以使用kill -3 命令。您还可以使用 jstack 实用程序;它对进程 ID 进行操作,您可以使用另一个 jps 实用程序找到该进程 ID。
  5. 什么 JVM 参数用于控制线程的堆栈大小?

  6. 这是简单的 -Xss 参数之一,用于控制 Java 中线程堆栈的大小。
  7. 同步锁和可重入锁的区别?

  8. 有时,实现互斥的唯一方法是通过synchronized关键字,但它有几个缺点,例如无法将锁扩展到方法或代码块之外,等等。Java 5 通过 Lock 接口提供更细粒度的控制来解决这个问题。ReentrantLock 是一种常见的 Lock 实现,它为 Lock 提供与隐式监视器相同的基本行为和语义,使用同步方法实现,但具有增强的功能。
  9. 给定 3 个线程 T1、T2 和 T3?如何实现序列T1、T2、T3?

  10. 可以通过多种方式实现一致性,但您可以简单地使用 join() 方法在另一个线程完成执行时启动一个线程。要实现给定的序列,需要先启动最后一个线程,然后以相反的顺序调用 join() 方法,即 T3 调用 T2.join,T2 调用 T1.join,因此 T1 先完成,T3 最后完成。
  11. 收益率法有什么作用?

  12. Yield 方法是要求线程放弃 CPU 以便另一个线程可以执行的一种方法。这是一个静态方法,它只保证当前线程让出处理器,但不决定去哪个线程执行。
  13. ConcurrentHashMap的并发级别是多少?

  14. ConcurrentHashMap 通过将实际映射分成多个部分来实现其可扩展性和线程安全性。这种分离是通过使用一定程度的并行性来实现的。这是 ConcurrentHashMap 构造函数的可选参数,其默认值为 16。
  15. 什么是信号量?

  16. 信号量是一种新型的同步器。这是一个带有计数器的信号量。从概念上讲,信号量控制一组权限。如有必要,每个 acquire() 在权限可用之前都会阻塞,然后获取它。每个release()都会添加一个权限,可能会释放阻塞的获取者。然而,这并没有使用实际的权限对象;信号量只是存储可用信号量的数量并采取相应的行动。信号量用于保护数量有限的昂贵资源,例如到池数据库的连接。
  17. 如果线程池队列已满并且您提交任务会发生什么情况?

  18. 如果线程池队列已满,则提交的任务将被“拒绝”。ThreadPoolExecutor的submit()方法抛出RejectedExecutionException,之后调用RejectedExecutionHandler。
  19. 线程池上的submit()和execute()方法之间的区别?

  20. 这两种方法都是向线程池提交任务的方法,但是它们之间有细微的区别。Execute(Runnable command) 定义在 Executor 接口中,并在将来执行给定的任务,但更重要的是,不返回任何内容。另一方面,submit() 是一个重载方法,它可以接受 Runnable 和 Callable 任务,并可以返回一个 Future 对象,该对象可用于取消执行和/或等待计算结果。该方法定义在ExecutorService接口中,该接口继承自Executor接口,每个线程池类,如ThreadPoolExecutor或ScheduledThreadPoolExecutor都继承了这些方法。
  21. 什么是阻塞方法?

  22. 阻塞方法是阻塞直到任务完成的方法,例如ServerSocketaccept()方法在等待客户端连接时阻塞。这里,阻塞意味着在作业完成之前控制不会返回到调用方法。另一方面,有一些异步或非阻塞方法在任务完成之前完成。
  23. Swing 线程安全吗?

  24. 简单地说,不,Swing 不是线程安全的,但是你需要解释你的意思,即使面试官没有问。当我们说Swing不是线程安全的时候,我们通常指的是它是一个不能被多线程修改的组件。对 GUI 组件的所有更改都必须在 AWT 线程中进行,Swing 提供了同步和异步方法来调度此类更改。
  25. invokeAndWait 和 invokeLater 之间的区别?

  26. 这两个 Swing API 方法允许开发人员从线程而不是从事件管理器线程更新 GUI 组件。InvokeAndWait() 同步更新 GUI 组件,例如进度条;每次取得进度时,必须更新进度条以反映更改。如果在另一个线程中跟踪进度,则必须调用 invokeAndWait() 来分配事件调度程序线程来更新该组件。而invokeLater()是更新组件的异步调用。
  27. 哪些 Swing API 方法是线程安全的?

  28. 这个问题又是关于Swing和线程安全的,虽然Swing组件不是线程安全的,但是有一些方法可以从多个线程安全地调用。我知道 repaint() 和 revalidate() 是线程安全的,但是不同的 Swing 组件上还有其他方法,例如 JTextComponent 的 setText() 和 JTextArea 的 insert() 和 append() 方法。
  29. 如何创建不可变对象?

  30. 这个问题看似与多线程和并发无关,但其实确实如此。不变性有助于简化已经复杂的并行代码。不可变对象对于开发人员来说非常昂贵,因为它可以在没有任何同步的情况下进行传播。不幸的是,Java没有@Immutable注解,这将使你的对象不可变,为此开发人员需要努力工作。要创建不可变对象,您需要遵循一些基础知识:在构造函数中进行初始化、没有 setter、没有引用泄漏、存储可变对象的单独副本。
  31. 什么是读写锁?

  32. 一般来说,ReadWriteLock 是锁解析技术的结果,旨在提高并行应用程序的性能。这是Java 5中添加的接口。它操作一对相关的锁,一个用于读操作,一个用于写操作。读取器锁可以由多个读取线程同时持有,直到没有写入器为止。写锁是独占的。如果需要,您可以使用规则集实现一个接口,也可以使用 ReentrantReadWriteLock,它最多支持 65535 个递归写锁和 65535 个读锁。
  33. 什么是忙旋转?

  34. Busy Spin 是程序员用来强制线程在特定条件下等待的一种技术。与涉及放弃处理器控制的传统方法 wait()、sleep() 或 Yield() 不同,此方法不会放弃处理器;相反,它只是执行一个空循环。为什么有人会这样做?保存处理器缓存。在多核系统上,挂起的线程可能会继续在另一个核心上执行,这意味着缓存重建。为了避免代价高昂的重建,程序员更喜欢通过使用繁忙自旋来减少等待。
  35. 易失性变量和原子变量之间的区别?

  36. 这是一个相当有趣的问题,乍一看,挥发性变量和原子变量看起来很相似,但它们仍然不同。Volatile 变量提供了发生在之前的保证,即写入将在任何后续写入之前发生;它不保证原子性。例如,count++操作不会仅仅因为count被声明为易失性而成为原子操作。另一方面,AtomicInteger 类提供了一个原子方法来原子地执行此类复杂操作,例如 getAndIncrement() 是增量运算符的原子替代,它可用于以原子方式将当前值加一。其他数据类型也有原子版本。
  37. 如果线程在同步块中抛出异常会发生什么?

  38. 对于普通 Java 程序员来说,这是另一个棘手的问题。无论您如何退出同步块,无论是正常地完成执行还是突然抛出异常,线程在进入同步块时都会释放所获取的锁。这就是为什么我更喜欢同步锁块而不是释放锁时需要特别小心的接口的原因之一,通常通过在finally块中释放锁来实现。
  39. 什么是 Singleton 的双重检查锁定?

  40. 这是最常见的面试问题之一,尽管它很受欢迎,但候选人回答它的机会最多只有 50%。他们一半的时间无法编写代码,另一半的时间则无法解释 Java 1.5 中的代码是如何被破坏和修复的。这是创建线程安全单例的一种旧方法,它试图通过仅在首次实例化单例实例时阻塞来优化性能,但由于其复杂性以及它在 JDK 1.4 中被破坏的事实,我个人不喜欢它。尽管如此,即使您不喜欢这种方法,从面试的角度了解它也是有用的。
  41. 如何创建线程安全的单例?

  42. 这个问题是对上一个问题的补充。如果你说你不喜欢双重检查锁定,那么面试官将被迫询问创建线程安全单例的替代方法。而且它们是,您可以利用类加载和静态变量初始化功能来实例化 Singleton,也可以利用强大的枚举类型。
  43. 列出您在并行编程中遵循的 3 个习惯?

  44. 这是我最喜欢的问题,因为我相信编写并行代码时需要遵循某些规则,这有助于提高性能、调试和支持。以下是我认为每个 Java 程序员都应该遵循的 3 条最佳规则:
    • 始终为您的线程指定有意义的名称
    • 在并行代码中查找错误或跟踪异常是一项相当困难的任务。OrderProcessor、QuoteProcessor 或 TradeProcessor 比 Thread-1 好得多。线程 2 和线程 3。该名称应反映线程执行的任务。所有主流框架甚至JDK都遵循这个规则。
    • 避免阻塞或缩小同步范围
    • 阻塞的成本很高,而上下文切换的成本则更高。尽量避免同步和阻塞,这样您就可以将临界区减少到所需的最低限度。这就是为什么我更喜欢定时阻止而不是定时方法,因为它可以让您对阻止的程度进行绝对控制。
    • 在同步器和等待和通知之间,选择同步器
    • 首先,CountDownLatch、Semaphore、CyclicBarrier 或 Exchanger 等同步器简化了编码。使用wait和notify来实现复杂的控制流程是非常困难的。其次,这些类是由业内最优秀的人员编写和维护的,并且它们很有可能在未来的 JDK 版本中被优化或替换为更好的代码。通过使用高级同步实用程序,您将自动获得所有这些好处。
    • 在并发收集和同步收集之间,选择并发收集
    • 这是另一个简单的规则,很容易遵循并获得好处。并发集合比同步集合更具可扩展性,因此在编写并行代码时最好使用它们。因此,下次您需要映射时,请先考虑 ConcurrentHashMap,然后再考虑 Hashtable。
  45. 如何强制启动一个线程?

  46. 这是一个如何强制垃圾收集运行的问题。简而言之,没办法,你当然可以使用System.gc()进行查询,但它不能保证任何东西。在Java中绝对没有办法强制线程启动,这是由线程调度程序控制的,并且Java没有提供任何API来控制它。Java的这部分仍然是随机的。
  47. 什么是 Fork/Join 框架?

  48. JDK 7 中引入的 Fork/Join 框架是一个强大的实用程序,允许开发人员利用现代服务器的多个处理器。它是为可以递归分解为小颗粒的工作而设计的。目标是利用所有可用的计算能力来提高应用程序的性能。该框架的一个显着优点是它使用工作窃取算法(从工作 - 工作和窃取 - 窃取)。已经用完作业的工作线程可以从仍然繁忙的其他线程中窃取作业。
  49. 调用 wait() 和 sleep() 有什么区别?

  50. 尽管 wait 和 sleep 都代表 Java 应用程序中的一种暂停,但它们是满足不同需求的设备。等待用于内部线程通信,如果等待条件为真,则它会产生锁定,如果另一个线程的操作使等待条件为假,则等待通知。另一方面,sleep() 方法只是放弃处理器或停止当前线程执行指定的时间。调用 sleep() 不会释放当前线程持有的锁。
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION