您可以参加任何Java面试,无论是大四还是中级,经验或新来的人,一定会看到线程,并发和多线程中的几个问题。 实际上,这种内置的并发支持是Java编程语言的最强优势之一,并帮助它在企业界和程序员中同样流行。 大多数有利可图的Java开发人员职位都需要出色的Java多核核心技能,以及在开发,调试和调整高性能低延迟并发Java应用程序方面的经验。
这就是原因,它是面试中最受欢迎的技能之一。 在典型的Java采访中,Interviewer通过询问以下问题逐渐从Thread的基本概念开始,例如您为什么需要线程,如何创建线程,这是一种更好的创建线程的方法,例如通过扩展线程类或实现Runnable ,然后慢慢进入并发问题,并发Java应用程序开发过程中面临的挑战,Java内存模型,JDK 1.5中引入的高级并发实用程序,并发Java应用程序的原理和设计模式,经典的多线程问题,例如生产者消费者,就餐哲学家,读者作家或简单地有界缓冲区问题。 由于仅了解线程基础知识还不够,因此您必须知道如何处理并发问题,例如死锁 , 竞争条件 ,内存不一致以及各种与线程安全有关的问题。 通过提出各种多线程和并发问题,对这些技能进行了全面测试。 许多Java开发人员习惯于在进行面试之前仅阅读和阅读面试问题,这还不错,但是您应该相距不远。 同样,收集问题并进行相同的练习也很费时间,这就是为什么我创建了这份清单,该清单是从各种采访中收集的与Java多线程和并发相关的前50个问题 。 当我发现它们时,我只会添加新的和最近的面试问题。 顺便说一句,我还没有在这里提供这个问题的答案,为什么? 因为我希望大多数Java开发人员都知道该问题的答案,如果不是,那么使用Google也可以广泛获得答案。 如果找不到任何特定问题的答案,可以随时在评论部分询问我。 您甚至可以在提供的链接上找到一些问题的答案,也可以在我之前的文章“带答案的12个Java线程问题”中找到答案 。
这是我们有关Java线程,并发和多线程的主要问题列表。 您可以使用此列表为Java面试做好准备。
- Java中的线程是什么?
线程是独立的执行路径。 这是一种利用计算机中可用的多个CPU的方式。 通过使用多个线程,可以加快CPU绑定任务的速度。 例如,如果一个线程完成一项工作需要100毫秒,则可以使用10个线程将该任务减少到10毫秒。 Java在语言级别为多线程提供了出色的支持,并且它也是卖点之一。 有关更多详细信息, 请参见此处 。
- Java中的线程和进程之间的区别?
线程是进程的子集,换句话说,一个进程可以包含多个线程。 两个进程在不同的内存空间上运行,但是所有线程共享相同的内存空间。 不要将此与堆栈内存混淆,堆栈内存对于不同的线程是不同的,并且用于将本地数据存储到该线程。 有关更多详细信息,请参见此答案 。
- 您如何在Java中实现线程?
在语言级别,有两种方法可以用Java实现Thread。 java.lang.Thread的一个实例表示一个线程,但是它需要执行一个任务,它是接口java.lang.Runnable的实例。 由于Thread类本身实现Runnable,因此可以通过扩展Thread类或仅实现Runnable接口来覆盖run()方法。 有关详细的答案和讨论,请参见本文 。
- 何时在Java中使用Runnable vs Thread?
这是先前的多线程面试问题的跟进。 众所周知,我们可以通过扩展Thread类或实现Runnable接口来实现线程,出现问题,哪种更好,什么时候使用? 如果您知道Java编程语言不支持类的多重继承,但是可以使用它实现多个接口,那么这个问题将很容易回答。 这意味着,如果您还想扩展另一个类(例如Canvas或CommandListener),则实现Runnable比扩展Thread更好。 有关更多观点和讨论,您也可以参考这篇文章 。
- Thread类的start()和run()方法之间的区别?
早期的一个棘手的Java问题,但仍然足以区分对Java线程模型的浅浅理解start()方法用于启动新创建的线程,而start()内部调用run()方法,调用run有区别()方法直接。 当您以常规方法调用run()时,该方法在同一线程中调用,因此不会启动新线程,这是您调用start()方法时的情况。 阅读此答案进行更详细的讨论。
- Java中Runnable和Callable之间的区别?
Runnable和Callable都表示打算在单独的线程中执行的任务。 JDK 1.0提供了Runnable,而JDK 1.5则添加了Callable。 两者之间的主要区别在于Callable的call()方法可以返回值并引发Exception,而Runnable的run()方法则无法实现。 可调用的返回Future对象,可以保存计算结果。 有关此问题的更深入解答,请参阅我关于相同主题的博客文章 。
- Java中CyclicBarrier和CountDownLatch之间的区别?
尽管CyclicBarrier和CountDownLatch都在一个或多个事件上等待线程数,但是它们之间的主要区别是,一旦计数达到零,您就无法重用CountDownLatch,但是即使在屏障被破坏后,您也可以重用相同的CyclicBarrier。 有关其他几点和示例代码示例,请参见此答案 。
- 什么是Java内存模型?
Java内存模型是一组规则和准则,可让Java程序在多个内存体系结构,CPU和操作系统之间确定性地运行。 在多线程的情况下尤其重要。 Java内存模型提供了某种保证,即一个线程所做的更改应对其他线程可见,其中之一是发生在关系之前。 这种关系定义了一些规则,这些规则使程序员可以预期并推理并发Java程序的行为。 例如,之前发生的关系保证:
- 线程中的每个动作都发生-在该线程中的每个动作之后,它们在程序顺序中排在后面,这称为程序顺序规则。
- 监视器锁发生解锁-在同一监视器锁上的每个后续锁之前,也称为监视器锁规则。
- 在随后每次对该字段的读取之前,都会对volatile字段进行写操作,这称为“可变变量规则”。
- 线程上对Thread.start的调用发生-在任何其他线程检测到线程已终止之前,通过成功从Thread.join()返回或通过Thread.isAlive()返回false(也称为线程启动规则)来进行。
- 一个线程在另一个线程上调用中断发生-被中断的线程在检测到中断之前(通过引发InterruptedException或调用isInterrupted或被中断),通常称为线程中断规则。
- 对象构造函数的结束发生在该对象的终结器开始之前,即终结器规则。
- 如果A发生在B之前,而B发生在C之前,那么A发生在C之前,这意味着在发生之前,保证了传递性。
我强烈建议阅读《 Java并发实践》第16章,以更详细地了解Java内存模型。
- 什么是Java中的volatile变量?
volatile是一个特殊的修饰符,只能与实例变量一起使用。 在并发Java程序中,在没有任何同步器(例如,同步关键字或锁)的情况下,其他人看不到多个线程对实例变量进行的更改。 如前一个问题中的“易变变量规则”所述, 易变变量保证写操作将在后续读取之前发生。 阅读此答案以了解有关volatile变量以及何时使用它们的更多信息。
- 什么是线程安全? Vector是线程安全的类吗?
(是的,请参阅详细信息 )
线程安全性是对象或代码的属性,它保证如果多线程以任何方式(例如,读写)执行或使用该线程或对象,其行为将与预期的一样。 例如,如果在多个线程之间共享该计数器的同一实例,则线程安全计数器对象将不会丢失任何计数。 显然,您也可以将集合类分为两类,线程安全的和非线程安全的。 Vector确实是线程安全的类,它通过同步修改Vector状态的方法来实现线程安全,另一方面,其对应的ArrayList不是线程安全的。
- Java中的竞争条件是什么? 举一个例子?
当Java程序处于并发执行环境中时,争用条件是一些细微的编程错误的原因。 顾名思义,竞争条件是由于多个线程之间的竞争而产生的,如果本应首先执行的线程丢失了竞争,然后执行了第二,则代码的行为会发生变化,这会成为不确定的错误。 由于线程之间竞争的随机性,这是最难发现和再现的错误之一。 竞争条件的一个示例是乱序处理,有关Java程序中竞争条件的更多示例,请参见此答案 。
- 如何在Java中停止线程?
我总是说Java为所有事物提供了丰富的API,但具有讽刺意味的是Java没有提供确定的停止线程的可靠方法。 JDK 1.0中有一些控制方法,例如stop(),suspend()和resume(),由于潜在的死锁威胁而在以后的版本中不推荐使用,从那时起,Java API设计人员就一直没有努力提供一致的,线程安全的和优雅的方式来停止线程。 程序员主要依靠这样的事实,即线程一旦完成run()或call()方法的执行就自动停止。 要手动停止,程序员可以利用易失的布尔变量,并在每次迭代中检查run方法是否具有循环或中断线程以突然取消任务。 有关在Java中停止线程的示例代码,请参见本教程 。
- 当线程中发生异常时会发生什么?
这是我在访谈中看到的一个棘手的Java问题 。 简而言之,如果未捕获的线程将死亡,如果注册了未捕获的异常处理程序,则它将获得回调。 Thread.UncaughtExceptionHandler是一个接口,定义为当线程由于未捕获的异常突然终止时调用的处理程序的嵌套接口。 当线程由于未捕获的异常而即将终止时,Java虚拟机将使用Thread.getUncaughtExceptionHandler()在线程中查询其UncaughtExceptionHandler并将调用处理程序的uncaughtException()方法,并将线程和异常作为参数传递。
- 您如何在Java中的两个线程之间共享数据?
您可以使用共享对象或并发数据结构(例如BlockingQueue)在线程之间共享数据。 请参阅本教程以学习Java中的线程间通信 。 它使用等待和通知方法来实现Producer使用者模式,该模式涉及在两个线程之间共享对象。
- Java中notify和notifyAll之间的区别?
这是Java核心访谈中另一个棘手的问题,因为多个线程可以等待单个监视器锁定,所以Java API设计器提供了一种方法,用于在等待条件改变后仅通知其中一个或所有通知,但是它们提供了一半的实现。 那里的notify()方法没有提供任何选择特定线程的方法,这就是为什么它仅在您知道只有一个线程正在等待时才有用。 另一方面,notifyAll()向所有线程发送通知,并允许它们竞争锁,这确保了至少一个线程将继续进行。 有关更详细的答案和代码示例,请参见我在类似主题上的博客文章 。
- 为什么等待,notify和notifyAll不在线程类内?
这是一个与设计有关的问题,它检查候选人对现有系统的看法,或者他是否曾经想到过如此普遍但最初看起来不合适的事物。 为了回答这个问题,您必须给出一些原因,为什么这三个方法在Object类中有意义,而为什么不在Thread类中有意义。 显而易见的原因之一是Java在对象级别而不是线程级别提供了锁定。 每个对象都有锁,该锁由线程获取。 现在,如果线程需要等待某些锁定,则有必要在该对象而不是该线程上调用wait()。 如果在Thread类上声明了wait()方法,则不清楚哪个线程正在等待。 简而言之,由于wait,notify和notifyAll在锁级别进行操作,因此在对象类上定义它是有意义的,因为锁属于对象。 您也可以查看本文,以获取有关此问题的更详细的答案。
- Java中的ThreadLocal变量是什么?
ThreadLocal变量是Java程序员可以使用的特殊类型的变量。 就像实例变量是每个实例一样,ThreadLocal变量是每个线程。 这是一种实现昂贵创建对象的线程安全的好方法,例如,您可以使用ThreadLocal使SimpleDateFormat成为线程安全的。 由于该类很昂贵,因此在本地范围内使用它不是很好,因为每次调用都需要单独的实例。 通过为每个线程提供自己的副本,您可以用一个箭头射杀两只鸟。 首先,通过重用固定数量的实例来减少昂贵对象的实例数,其次,无需支付同步或不变性即可实现线程安全。 线程局部变量的另一个很好的例子是ThreadLocalRandom类,它减少了在多线程环境中创建昂贵的Random对象的实例数量。 请参阅此答案以了解有关Java中线程局部变量的更多信息。
- Java中的FutureTask是什么?
FutureTask表示并发Java应用程序中的可取消异步计算。 此类提供Future的基本实现,其中包含启动和取消计算,查询以查看计算是否完成以及检索计算结果的方法。 只有在计算完成后才能检索结果; 如果计算尚未完成,则get方法将阻塞。 FutureTask对象可用于包装Callable或Runnable对象。 从FutureTask开始
还实现了Runnable,可以将其提交给执行器以执行。 - Java中interrupted和isInterrupted方法之间的区别?
interrupted()和isInterrupted()之间的主要区别在于,前者清除中断状态,而后者不清除。 Java多线程中的中断机制是使用称为中断状态的内部标志实现的。 通过调用Thread.interrupt()来中断线程会设置此标志。 当被中断的线程通过调用静态方法 Thread.interrupted()检查中断时,将清除中断状态。 一个线程用于查询另一线程的中断状态的非静态isInterrupted()方法不会更改中断状态标志。 按照惯例,任何通过抛出InterruptedException退出的方法都会清除中断状态。 但是,总是有可能通过另一个调用中断的线程立即再次设置中断状态。
- 为什么从同步块中调用wait和notify方法?
从同步块或方法中调用wait和notify方法的主要原因是Java API强制了它。 如果不从同步上下文中调用它们,则代码将引发IllegalMonitorStateException。 一个更微妙的原因是避免在等待和通知呼叫之间出现竞争状态。 要了解更多信息,请在此处查看我的同名帖子。
- 为什么要检查循环等待条件?
等待中的线程可能会收到错误警报和虚假的唤醒调用,如果它不检查循环中的等待条件,即使不满足条件,它也只会退出。 这样,当等待线程唤醒时,它不能假定其正在等待的状态仍然有效。 它可能在过去是有效的,但是在调用notify()方法之后以及在等待线程唤醒之前,状态可能已经更改。 这就是从循环调用wait()方法总是更好的原因,您甚至可以创建用于调用wait的模板并在Eclipse中进行通知。 要了解有关此问题的更多信息,建议您阅读有关线程和同步的有效Java项目。
- Java中同步和并发收集之间的区别?
尽管同步和并发收集都提供了适用于多线程和并发访问的线程安全收集,但是后期比以前更具可伸缩性。 在Java 1.5之前,Java程序员只有同步收集,如果多个线程同时访问它们,它们将成为争用的源,这会妨碍系统的可伸缩性。 Java 5引入了诸如ConcurrentHashMap之类的并发集合,该集合不仅提供线程安全性,而且还通过使用锁剥离和对内部表进行分区等现代技术来提高可伸缩性。 有关Java中同步和并发收集之间的更多区别,请参见此答案 。
- Java中的堆栈和堆之间的区别?
为什么有人将这个问题作为多线程和并发的一部分? 因为堆栈是与线程紧密相关的内存区域。 为了回答这个问题,堆栈和堆都是Java应用程序中的特定内存。 每个线程都有自己的堆栈,该堆栈用于存储局部变量,方法参数和调用堆栈。 存储在一个线程堆栈中的变量对其他线程不可见。 另一方面,堆是所有线程共享的公共内存区域。 在堆内部创建本地或任何级别的对象。 为了提高性能,线程倾向于将值从堆中缓存到堆栈中,如果该变量被多个线程修改,则可能会产生问题,这就是易变变量的所在。 volatile建议线程始终从主内存中读取变量的值。 请参阅本文以了解有关Java中堆栈和堆的更多信息,以更详细地回答此问题。
- 什么是线程池? 为什么要在Java中使用线程池?
就时间和资源而言,创建线程是昂贵的。 如果在请求处理时创建线程,这会减慢响应时间,并且一个进程只能创建数量有限的线程。 为避免这两个问题,在应用程序启动时将创建线程池,并将线程重用于请求处理。 该线程池称为“线程池”,而线程称为工作线程。 从JDK 1.5版本开始,Java API提供了Executor框架,该框架允许您创建不同类型的线程池,例如单个线程池(一次处理一个任务),固定线程池(固定数量的线程池)或缓存的线程池。 (一个可扩展的线程池,适用于具有许多短期任务的应用程序)。 请参阅本文以了解有关Java中线程池的更多信息,以准备该问题的详细答案。
- 编写代码来解决Java中的Producer Consumer问题?
您在现实世界中解决的大多数线程问题都属于Producer使用者模式类别,其中一个线程正在执行任务,而另一个线程正在使用该线程。 您必须知道如何进行线程间通信以解决此问题。 在最低级别上,您可以使用wait和notify来解决此问题,在最高级别上,您可以利用Semaphore或BlockingQueue来实现Producer使用者模式,如本教程所示。
- 如何避免Java死锁? 写代码?
死锁是指两个线程互相等待以采取行动,从而允许它们进一步移动的情况。 这是一个严重的问题,因为发生这种情况时,您的程序会挂起,并且没有执行预期的任务。 为了使死锁发生,必须满足以下四个条件:- 互斥:至少一种资源必须以不可共享的方式持有。 在任何给定时刻,只有一个进程可以使用资源。
- 保留并等待:一个进程当前正在保存至少一个资源,并请求其他进程正在保留的其他资源。
- 否抢占:一旦分配了资源,操作系统不得取消分配资源。 它们必须由持有程序自愿释放。
- 循环等待:一个进程必须等待另一个进程正在占用的资源,而另一个进程又在等待第一个进程释放该资源。
避免死锁的最简单方法是防止循环退出 ,这可以通过以特定顺序获取锁并以相反的顺序释放锁来完成,以便线程仅在持有另一个时才能继续获取锁。 在本教程中查看实际的代码示例,以及有关避免Java中死锁的技术的详细讨论。
- Java中的活锁和死锁之间的区别?
这个问题是前一次面试问题的延伸。 活锁类似于死锁,不同之处在于,活锁中涉及的线程或进程的状态彼此之间不断变化,而没有任何一个进一步发展。 Livelock是资源匮乏的特例。 当两个人在狭窄的走廊里相遇时,发生了现实生活中的活锁例子,每个人都试图通过移动到一边让对方通过而礼貌,但最终却没有任何进展就左右摇摆,因为他们都反复移动在同一时间相同的方式。 简而言之,活动锁和死锁之间的主要区别在于,在先前的过程更改状态下,没有任何进展。
- 如何检查线程是否持有锁?
我什至不知道您可以在Java访谈的电话回合中问到这个问题之前,我是否可以检查线程是否已经锁定。 在java.lang.Thread上有一个名为holdLock()的方法,当且仅当当前线程持有对指定对象的监视器锁时,它才返回true。 您也可以查看本文以获得更详细的答案 。
- 您如何在Java中进行线程转储?
根据操作系统的不同,可以采取多种方式进行Java进程的线程转储。 进行线程转储时,JVM会在日志文件或标准错误控制台中转储所有线程的状态。 在Windows中,您可以使用Ctrl + Break键组合进行线程转储,而在Linux上,您可以使用kill -3命令。 您还可以使用名为jstack的工具进行线程转储,它对进程ID进行操作,可以使用另一个名为jps的工具找到该进程。
- 哪个JVM参数用于控制线程的堆栈大小?
这是简单的一个,-Xss参数用于控制Java中Thread的堆栈大小。 您可以查看此JVM选项列表,以了解有关此参数的更多信息。
- Java中Synchronized和ReentrantLock之间的区别?
曾经有几天,在Java中提供互斥的唯一方法是通过synced关键字,但是它有几个缺点,例如您不能将锁扩展到方法或块边界之外,不能放弃尝试锁等。Java5解决了这个问题通过Lock接口提供更复杂的控制来解决问题。 ReentrantLock是Lock接口的常见实现,它提供与可同步方法和语句访问的隐式监视器锁相同的基本行为和语义的可重入互斥锁,但具有扩展功能。 请参阅本文,以了解这些功能以及Java中Synchronized与ReentrantLock之间的更多区别。
- 有T1,T2和T3三个线程吗? 您如何确保Java中的序列T1,T2,T3?
多线程中的排序可以通过不同的方法来实现,但是您可以简单地使用线程类的join()方法在另一个线程完成执行时启动一个线程。 为了确保执行三个线程,您需要首先启动最后一个线程,例如T3,然后以相反的顺序调用join方法,例如T3调用T2。 join,然后T2调用T1.join,这样T1将首先完成,而T3将最后完成。 要了解有关join方法的更多信息,请参阅本教程 。
- Thread类的yield方法有什么作用?
Yield方法是请求当前线程放弃CPU以便其他线程可以执行的一种方法。 Yield是一种静态方法,仅保证当前线程将放弃CPU,但未说明任何其他线程将获得CPU。 同一线程可能会收回CPU并重新开始执行。 请参阅本文,以了解有关屈服方法的更多信息并更好地回答此问题。
- Java中ConcurrentHashMap的并发级别是多少?
ConcurrentHashMap通过将实际映射划分为多个部分来实现其可伸缩性和线程安全性。 使用并发级别可以实现此分区。 它是ConcurrentHashMap构造函数的可选参数,默认值为16。表在内部进行了分区,以尝试允许指定数量的并发更新而不会发生争用。 要了解有关并发级别和内部调整大小的更多信息,请参阅我的文章ConcurrentHashMap如何在Java中工作 。
- Java中的信号量是什么?
Java中的信号量是一种新型的同步器。 这是一个计数信号量。 从概念上讲,信号量维护一组许可证。 如有必要,每个Acquisition()都会阻塞,直到获得许可为止,然后再获取许可。 每个release()添加一个许可,有可能释放阻塞获取者。 但是,没有使用实际的许可对象。 信号量只是保持可用数量的计数并采取相应措施。 信号量用于保护固定数量的可用昂贵资源,例如池中的数据库连接。 请参阅本文,以了解有关在Java中对信号量进行计数的更多信息。
- 如果线程池队列已满,如果提交任务会怎样?
这是我列表中另一个棘手的问题。 许多程序员会认为它会阻塞直到任务被清除,但是它是正确的。 如果无法安排执行任务,则ThreadPoolExecutor的Submit()方法将抛出RejectedExecutionException。
- Java中submit()和execute()方法线程池之间的区别?
两种方法都是将任务提交到线程池的方法,但是两者之间略有不同。 execute(Runnable command)在Executor接口中定义,将来会执行给定的任务,但更重要的是它不返回任何内容。 它的返回类型为void。 另一方面,submit()是重载的方法,它可以执行Runnable或Callable任务,并且可以返回可以保存未决计算结果的Future对象。 此方法在ExecutorService接口上定义,该接口扩展了Executor接口,并且其他所有线程池类(例如ThreadPoolExecutor或ScheduledThreadPoolExecutor)都将获得这些方法。 要了解有关线程池的更多信息,可以查看本文 。
- Java中的阻塞方法是什么?
阻塞方法是一种阻塞直到任务完成的方法,例如ServerSocket的accept()方法阻塞直到连接了客户端。 在这里阻塞意味着直到任务完成之前控制权不会返回给调用者。 另一方面,存在异步或非阻塞方法,该方法甚至在任务完成之前就返回。 要了解有关阻塞方法的更多信息,请参见此答案 。
- Swing线程安全吗? Swing线程安全是什么意思?
您可以简单地将这个问题表示为“不,Swing不是线程安全的”,但是即使面试官没有询问,您也必须解释您的意思。 当我们说swing不是线程安全的时,我们通常引用其组件,该组件不能在多个线程中进行修改。 对GUI组件的所有更新都必须在AWT线程上完成,Swing提供了同步和异步回调方法来安排此类更新。 您也可以阅读我的文章,以了解有关摆动和线程安全的更多信息,以更好地回答此问题。 甚至接下来的两个问题也与此概念相关。
- Java中invokeAndWait和invokeLater之间的区别?
这是Swing API为Java开发人员提供的两种方法,可从事件分发程序线程以外的线程更新GUI组件。 InvokeAndWait()同步更新GUI组件,例如进度条,一旦取得进度,也应更新该条以反映该更改。 如果在另一个线程中跟踪进度,则必须调用invokeAndWait()来调度事件分派器线程对该组件的更新。 另一方面,invokeLater()是异步调用以更新组件。 您也可以参考此答案以获取更多信息。
- 哪种Swing API方法在Java中是线程安全的?
这个问题再次与摆动和线程安全有关,尽管组件不是线程安全的,但是某些方法可以从多个线程安全地调用。 我知道repaint()和revalidate()是线程安全的,但是在不同的swing组件上还有其他方法,例如JTextComponent的setText()方法,JTextArea类的insert()和append()方法。
- 如何在Java中创建不可变对象?
这个问题看似与多线程和并发无关,但确实如此。 不变性有助于简化Java中已经很复杂的并发代码。 由于不共享对象可以共享而无需任何同步,因此Java开发人员非常重视。 旨在在线程之间共享的核心值对象应该是不变的,以提高性能和简化性。 不幸的是,Java中没有@Immutable批注,它可以使您的对象不可变,Java开发人员必须完成艰苦的工作。 您需要保留一些基本知识,例如在构造函数中初始化状态,没有setter方法,没有引用泄漏,保留可变对象的单独副本以创建Immutable对象。 有关逐步指南的信息,请参阅我的文章, 如何使对象在Java中不可变 。 这将为您提供足够的材料来自信地回答这个问题。
- 什么是Java中的ReadWriteLock?
通常,读写锁是锁剥离技术的结果,可以提高并发应用程序的性能。 在Java中,ReadWriteLock是在Java 5版本中添加的接口。 ReadWriteLock维护一对关联的锁,一个用于只读操作,一个用于写入。 只要没有写程序,读锁就可以同时由多个读程序线程保持。 写锁是排他的。 如果愿意,可以使用自己的一组规则来实现此接口,否则可以使用JDK随附的ReentrantReadWriteLock,它最多支持65535递归写锁和65535读锁。
- 什么是多线程忙碌中的自旋?
繁忙旋转是并发程序员用来使线程在特定条件下等待的技术。 Unlike traditional methods eg wait(), sleep() or yield() which all involves relinquishing CPU control, this method does not relinquish CPU, instead it just runs empty loop. Why would someone do that? to preserve CPU caches. In multi core system, its possible for a paused thread to resume on different core, which means rebuilding cache again. To avoid cost of rebuilding cache, programmer prefer to wait for much smaller time doing busy spin. You can also see this answer to learn more about this question.
- Difference between volatile and atomic variable in Java?
This is an interesting question for Java programmer, at first, volatile and atomic variable look very similar, but they are different. Volatile variable provides you happens-before guarantee that a write will happen before any subsequent write, it doesn't guarantee atomicity. For example count++ operation will not become atomic just by declaring count variable as volatile. On the other hand AtomicInteger class provides atomic method to perform such compound operation atomically eg getAndIncrement() is atomic replacement of increment operator. It can be used to atomically increment current value by one. Similarly you have atomic version for other data type and reference variable as well.
- What happens if a thread throws an Exception inside synchronized block?
This is one more tricky question for average Java programmer, if he can bring the fact about whether lock is released or not is key indicator of his understanding. To answer this question, no matter how you exist synchronized block, either normally by finishing execution or abruptly by throwing exception, thread releases the lock it acquired while entering that synchronized block. This is actually one of the reason I like synchronized block over lock interface, which requires explicit attention to release lock, generally this is achieved by releasing lock in finally block .
- What is double checked locking of Singleton?
This is one of the very popular question on Java interviews, and despite its popularity, chances of candidate answering this question satisfactory is only 50%. Half of the time, they failed to write code for double checked locking and half of the time they failed how it was broken and fixed on Java 1.5. This is actually an old way of creating thread-safe singleton, which tries to optimize performance by only locking when Singleton instance is created first time, but because of complexity and the fact it was broken for JDK 1.4, I personally don't like it. Anyway, even if you not prefer this approach its good to know from interview point of view. Since this question deserve a detailed answer, I have answered in a separate post, you can read my post how double checked locking on Singleton works to learn more about it.
- How to create thread-safe Singleton in Java?
This question is actually follow-up of previous question. If you say you don't like double checked locking then Interviewer is bound to ask about alternative ways of creating thread-safe Singleton class. There are actually man, you can take advantage of class loading and static variable initialization feature of JVM to create instance of Singleton, or you can leverage powerful enumeration type in Java to create Singleton. I actually preferred that way, you can also read this article to learn more about it and see some sample code.
- List down 3 multi-threading best practice you follow?
This is my favourite question, because I believe that you must follow certain best practices while writing concurrent code which helps in performance, debugging and maintenance. Following are three best practices, I think an average Java programmer should follow:
- Always give meaningful name to your thread This goes a long way to find a bug or trace an execution in concurrent code. OrderProcessor, QuoteProcessor or TradeProcessor is much better than Thread-1. Thread-2 and Thread-3. Name should say about task done by that thread. All major framework and even JDK follow this best practice.
- Avoid locking or Reduce scope of Synchronization
Locking is costly and context switching is even more costlier. Try to avoid synchronization and locking as much as possible and at bare minimum, you should reduce critical section. That's why I prefer synchronized block over synchronized method, because it gives you absolute control on scope of locking. - Prefer Synchronizers over wait and notify
Synchronizers like CountDownLatch, Semaphore, CyclicBarrier or Exchanger simplifies coding. It's very difficult to implement complex control flow right using wait and notify. Secondly, these classes are written and maintained by best in business and there is good chance that they are optimized or replaced by better performance code in subsequent JDK releases. By using higher level synchronization utilities, you automatically get all these benefits. - Prefer Concurrent Collection over Synchronized Collection
This is another simple best practice which is easy to follow but reap good benefits. Concurrent collection are more scalable than their synchronized counterpart, that's why its better to use them while writing concurrent code. So next time if you need map, think about ConcurrentHashMap before thinking Hashtable. See my article Concurrent Collections in Java , to learn more about modern collection classes and how to make best use of them.
- How do you force start a Thread in Java?
This question is like how do you force garbage collection in Java, their is no way, though you can make request using System.gc() but its not guaranteed. On Java multi-threading their is absolute no way to force start a thread, this is controlled by thread scheduler and Java exposes no API to control thread schedule. This is still a random bit in Java.
- What is fork join framework in Java?
The fork join framework, introduced in JDK 7 is a powerful tool available to Java developer to take advantage of multiple processors of modern day servers. 它是为可以递归分解为较小部分的工作而设计的。 目标是使用所有可用的处理能力来增强应用程序的性能。 One significant advantage of The fork/join framework is that it uses a work-stealing algorithm. 工作用尽的工作线程可以从其他仍很忙的线程中窃取任务。 See this article for much more detailed answer of this question.
- What is difference between calling wait() and sleep() method in Java multi-threading?
Though both wait and sleep introduce some form of pause in Java application, they are tool for different needs. Wait method is used for inter thread communication, it relinquish lock if waiting condition is true and wait for notification when due to action of another thread waiting condition becomes false. On the other hand sleep() method is just to relinquish CPU or stop execution of current thread for specified time duration. Calling sleep method doesn't release the lock held by current thread. You can also take look at this article to answer this question with more details.
That's all on this list of top 50 Java multi-threading and concurrency interview questions . I have not shared answers of all the questions but provided enough hints and links to explore further and find answers by yourselves. As I said, let me know if you don't find answer of any particular question and I will add answer here. You can use this list to not only to prepare for your core Java and programming interviews but also to check your knowledge about basics of threads, multi-threading, concurrency, design patterns and threading issues like race conditions, deadlock and thread safety problems. My intention is to make this list of question as mother of all list of Java Multi-threading questions, but this can not be done without your help. You can also share any question with us, which has been asked to you or any question for which you yet to find an answer. This master list is equally useful to Java developers of all levels of experience. You can read through this list even if you have 2 to 3 years of working experience as junior developer or 5 to 6 years as senior developer. It's even useful for freshers and beginners to expand their knowledge. I will add new and latest multi-threading question as and when I come across, and I request you all to ask, share and answer questions via comments to keep this list relevant for all Java programmers.
翻译自: https://www.javacodegeeks.com/2014/07/top-50-java-thread-interview-questions-answers-for-freshers-experienced-programmers.html