java并发多线程面试
Today we will go through Java Multithreading Interview Questions and Answers. We will also look into Concurrency interview questions and answers because both multithreading and concurrency go hand in hand.
今天,我们将讨论Java多线程面试问答。 我们还将研究并发面试问题和答案,因为多线程和并发是齐头并进的。
Thread is one of the popular topics in java interview questions. Here I am listing down most of the important java multithreading interview questions from interview perspective, but you should have good knowledge on java threads to deal with follow up questions.
线程是Java面试问题中的热门话题之一。 在这里,我从访谈的角度列出了大多数重要的Java多线程面试问题,但是您应该对Java线程有足够的知识来处理后续问题。
A process is a self contained execution environment and it can be seen as a program or application whereas Thread is a single task of execution within the process. Java runtime environment runs as a single process which contains different classes and programs as processes. Thread can be called lightweight process. Thread requires less resources to create and exists in the process, thread shares the process resources.
流程是一个自包含的执行环境,可以将其视为程序或应用程序,而线程是流程中执行的单个任务。 Java运行时环境作为单个进程运行,其中包含不同的类和程序作为进程。 线程可以称为轻量级进程。 线程需要较少的资源来创建和存在于进程中,线程共享进程资源。
In Multi-Threaded programming, multiple threads are executing concurrently that improves the performance because CPU is not idle incase some thread is waiting to get some resources. Multiple threads share the heap memory, so it’s good to create multiple threads to execute some task rather than creating multiple processes. For example, Servlets are better in performance than CGI because Servlet support multi-threading but CGI doesn’t.
在多线程编程中,多个线程可以同时执行,从而提高了性能,因为如果某些线程正在等待获取某些资源,则CPU不会处于空闲状态。 多个线程共享堆内存,因此最好创建多个线程来执行某些任务,而不要创建多个进程。 例如,Servlet的性能比CGI更好,因为Servlet支持多线程,但CGI不支持。
When we create a Thread in java program, it’s known as user thread. A daemon thread runs in background and doesn’t prevent JVM from terminating. When there are no user threads running, JVM shutdown the program and quits. A child thread created from daemon thread is also a daemon thread.
当我们在Java程序中创建线程时,它称为用户线程。 守护程序线程在后台运行,并且不会阻止JVM终止。 当没有用户线程在运行时,JVM会关闭程序并退出。 从守护程序线程创建的子线程也是守护程序线程。
There are two ways to create Thread in Java – first by implementing Runnable interface and then creating a Thread object from it and second is to extend the Thread Class. Read this post to learn more about creating threads in java.
有两种在Java中创建线程的方法-一种是通过实现Runnable接口,然后从该接口创建Thread对象,其二是扩展Thread Class。 阅读这篇文章,以了解有关在Java中创建线程的更多信息。
When we create a Thread in java program, its state is New. Then we start the thread that change it’s state to Runnable. Thread Scheduler is responsible to allocate CPU to threads in Runnable thread pool and change their state to Running. Other Thread states are Waiting, Blocked and Dead. Read this post to learn more about life cycle of thread.
当我们在Java程序中创建线程时,其状态为“新建”。 然后,我们启动将其状态更改为Runnable的线程。 线程调度程序负责将CPU分配给可运行线程池中的线程,并将其状态更改为正在运行。 其他线程状态为“等待”,“阻塞”和“死”。 阅读这篇文章,以了解有关线程生命周期的更多信息。
Yes, we can call run() method of a Thread class but then it will behave like a normal method. To actually execute it in a Thread, we need to start it using Thread.start() method.
是的,我们可以调用Thread类的run()方法,但是它的行为类似于普通方法。 要在线程中实际执行它,我们需要使用Thread.start()方法启动它。
We can use Thread class sleep() method to pause the execution of Thread for certain time. Note that this will not stop the processing of thread for specific time, once the thread awake from sleep, it’s state gets changed to runnable and based on thread scheduling, it gets executed.
我们可以使用Thread类sleep()方法将Thread的执行暂停一段时间。 请注意,这不会在特定时间内停止线程的处理,一旦线程从睡眠中醒来,其状态将更改为可运行,并根据线程调度执行该线程。
Every thread has a priority, usually higher priority thread gets precedence in execution but it depends on Thread Scheduler implementation that is OS dependent. We can specify the priority of thread but it doesn’t guarantee that higher priority thread will get executed before lower priority thread. Thread priority is an int whose value varies from 1 to 10 where 1 is the lowest priority thread and 10 is the highest priority thread.
每个线程都有一个优先级,通常更高优先级的线程会在执行中获得优先级,但它取决于依赖于OS的线程计划程序实现。 我们可以指定线程的优先级,但不能保证高优先级的线程会在低优先级的线程之前执行。 线程优先级是一个整数,其值在1到10之间变化,其中1是最低优先级线程,而10是最高优先级线程。
Thread Scheduler is the Operating System service that allocates the CPU time to the available runnable threads. Once we create and start a thread, it’s execution depends on the implementation of Thread Scheduler. Time Slicing is the process to divide the available CPU time to the available runnable threads. Allocation of CPU time to threads can be based on thread priority or the thread waiting for longer time will get more priority in getting CPU time. Thread scheduling can’t be controlled by java, so it’s always better to control it from application itself.
线程调度程序是一种操作系统服务,它将CPU时间分配给可用的可运行线程。 创建并启动线程后,其执行取决于Thread Scheduler的实现。 时间分片是将可用CPU时间划分为可用可运行线程的过程。 可以根据线程优先级为线程分配CPU时间,或者等待更长时间的线程将在获得CPU时间时获得更高的优先级。 线程调度不能由Java控制,因此始终最好从应用程序本身进行控制。
Context Switching is the process of storing and restoring of CPU state so that Thread execution can be resumed from the same point at a later point of time. Context Switching is the essential feature for multitasking operating system and support for multi-threaded environment.
上下文切换是存储和还原CPU状态的过程,以便可以在以后的某个时间点从同一点恢复线程执行。 上下文切换是多任务操作系统的基本功能,并且支持多线程环境。
We can use Thread join() method to make sure all the threads created by the program is dead before finishing the main function. Here is an article about Thread join method.
在完成main函数之前,我们可以使用Thread join()方法来确保程序创建的所有线程均已死。 这是有关线程连接方法的文章。
When threads share resources, communication between Threads is important to coordinate their efforts. Object class wait(), notify() and notifyAll() methods allows threads to communicate about the lock status of a resource. Check this post to learn more about thread wait, notify and notifyAll.
当线程共享资源时,线程之间的通信对于协调其工作很重要。 对象类的wait(),notify()和notifyAll()方法允许线程就资源的锁定状态进行通信。 检查这篇文章,以了解有关线程等待,通知和notifyAll的更多信息。
In Java every Object has a monitor and wait, notify methods are used to wait for the Object monitor or to notify other threads that Object monitor is free now. There is no monitor on threads in java and synchronization can be used with any Object, that’s why it’s part of Object class so that every class in java has these essential methods for inter thread communication.
在Java中,每个对象都有一个监视器并等待,通知方法用于等待对象监视器或通知其他线程该对象监视器现在是空闲的。 Java中没有线程监视程序,因此同步可以与任何Object一起使用,这就是为什么它是Object类的一部分的原因,因此Java中的每个类都具有这些线程间通信的基本方法。
When a Thread calls wait() on any Object, it must have the monitor on the Object that it will leave and goes in wait state until any other thread call notify() on this Object. Similarly when a thread calls notify() on any Object, it leaves the monitor on the Object and other waiting threads can get the monitor on the Object. Since all these methods require Thread to have the Object monitor, that can be achieved only by synchronization, they need to be called from synchronized method or block.
当线程在任何对象上调用wait()时,它必须在要离开的对象上具有监视器,并进入等待状态,直到对该对象上的任何其他线程调用notify()为止。 同样,当线程在任何对象上调用notify()时,它将监视器留在对象上,而其他等待线程可以在对象上获取监视器。 由于所有这些方法都要求线程具有对象监视器,这只能通过同步来实现,因此需要从同步方法或块中调用它们。
Thread sleep() and yield() methods work on the currently executing thread. So there is no point in invoking these methods on some other threads that are in wait state. That’s why these methods are made static so that when this method is called statically, it works on the current executing thread and avoid confusion to the programmers who might think that they can invoke these methods on some non-running threads.
线程sleep()和yield()方法在当前正在执行的线程上工作。 因此,在其他处于等待状态的线程上调用这些方法毫无意义。 这就是为什么将这些方法设置为静态的原因,以便当该方法被静态调用时,它可以在当前正在执行的线程上运行,并且避免使可能会认为可以在某些非运行线程上调用这些方法的程序员感到困惑。
There are several ways to achieve thread safety in java – synchronization, atomic concurrent classes, implementing concurrent Lock interface, using volatile keyword, using immutable classes and Thread safe classes. Learn more at thread safety tutorial.
有几种方法可以在Java中实现线程安全–同步,原子并发类,实现并发Lock接口,使用volatile关键字,使用不可变类和Thread安全类。 在线程安全教程中了解更多信息。
When we use volatile keyword with a variable, all the threads read it’s value directly from the memory and don’t cache it. This makes sure that the value read is the same as in the memory.
当我们将volatile关键字与变量一起使用时,所有线程都直接从内存中读取它的值,而不缓存它。 这样可以确保读取的值与存储器中的值相同。
Synchronized block is more preferred way because it doesn’t lock the Object, synchronized methods lock the Object and if there are multiple synchronization blocks in the class, even though they are not related, it will stop them from execution and put them in wait state to get the lock on Object.
同步块是更可取的方式,因为它不会锁定对象,同步方法会锁定对象,并且如果类中有多个同步块,即使它们不相关,也会使它们停止执行并将其置于等待状态获取对象的锁定。
Thread class setDaemon(true) can be used to create daemon thread in java. We need to call this method before calling start() method else it will throw IllegalThreadStateException.
线程类setDaemon(true)可用于在Java中创建守护程序线程。 我们需要在调用start()方法之前调用此方法,否则它将引发IllegalThreadStateException。
Java ThreadLocal is used to create thread-local variables. We know that all threads of an Object share it’s variables, so if the variable is not thread safe, we can use synchronization but if we want to avoid synchronization, we can use ThreadLocal variables.
Every thread has its own ThreadLocal variable and they can use it gets () and set() methods to get the default value or change it’s value local to Thread. ThreadLocal instances are typically private static fields in classes that wish to associate the state with a thread. Check this post for small example program showing ThreadLocal Example.
Java ThreadLocal用于创建线程局部变量。 我们知道对象的所有线程都共享它的变量,因此,如果变量不是线程安全的,则可以使用同步,但是如果要避免同步,则可以使用ThreadLocal变量。
每个线程都有自己的ThreadLocal变量,他们可以使用它的gets()和set()方法获取默认值或将其值更改为Thread本地。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段。 在这篇文章中查看显示ThreadLocal Example的小型示例程序。
ThreadGroup is a class which was intended to provide information about a thread group. ThreadGroup API is weak and it doesn’t have any functionality that is not provided by Thread. It has two main features – to get the list of active threads in a thread group and to set the uncaught exception handler for the thread. But Java 1.5 has added setUncaughtExceptionHandler(UncaughtExceptionHandler eh) method using which we can add uncaught exception handler to the thread. So ThreadGroup is obsolete and hence not advised to use anymore.
t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler(){
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("exception occured:"+e.getMessage());
}
});
ThreadGroup是一个类,旨在提供有关线程组的信息。 ThreadGroup API很弱,并且没有Thread未提供的任何功能。 它具有两个主要功能–获取线程组中活动线程的列表,并为该线程设置未捕获的异常处理程序。 但是Java 1.5添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh)方法,使用该方法可以将未捕获的异常处理程序添加到线程中。 因此ThreadGroup已过时,因此不建议再使用。
A thread dump is a list of all the threads active in the JVM, thread dumps are very helpful in analyzing bottlenecks in the application and analyzing deadlock situations. There are many ways using which we can generate Thread dump – Using Profiler, Kill -3 command, jstack tool, etc. I prefer jstack tool to generate thread dump of a program because it’s easy to use and comes with JDK installation. Since it’s a terminal-based tool, we can create a script to generate thread dump at regular intervals to analyze it later on. Read this post to know more about generating thread dump in java.
线程转储是JVM中所有活动线程的列表,线程转储对于分析应用程序中的瓶颈和分析死锁情况非常有帮助。 可以使用多种方法来生成线程转储–使用Profiler,Kill -3命令,jstack工具等。我更喜欢使用jstack工具来生成程序的线程转储,因为它易于使用并且随JDK安装一起提供。 由于它是基于终端的工具,因此我们可以创建脚本以定期生成线程转储,以供日后分析。 阅读这篇文章,以了解有关在Java中生成线程转储的更多信息。
Deadlock is a programming situation where two or more threads are blocked forever, this situation arises with at least two threads and two or more resources.
To analyze a deadlock, we need to look at the java thread dump of the application, we need to look out for the threads with state as BLOCKED and then the resources it’s waiting to lock, every resource has a unique ID using which we can find which thread is already holding the lock on the object.
Avoid Nested Locks, Lock Only What is Required and Avoid waiting indefinitely are common ways to avoid deadlock situation, read this post to learn how to analyze deadlock in java with a sample program.
死锁是两个线程或多个线程永远被阻塞的编程情况,这种情况发生在至少两个线程和两个或更多资源的情况下。
要分析死锁,我们需要查看应用程序的Java线程转储,我们需要查找状态为BLOCKED的线程,然后查找等待锁定的资源,每个资源都有一个唯一的ID,我们可以使用该ID来查找哪个线程已经对该对象进行了锁定。
避免嵌套锁,仅锁定所需内容和避免无限期等待是避免死锁的常见方法,请阅读此文章,以了解如何使用示例程序分析Java中的死锁 。
java.util.Timer is a utility class that can be used to schedule a thread to be executed at a certain time in future. Java Timer class can be used to schedule a task to be run one-time or to be run at regular intervals.
java.util.TimerTask is an abstract class that implements Runnable interface and we need to extend this class to create our own TimerTask that can be scheduled using java Timer class.
Check this post for java Timer example.
java.util.Timer是一个实用程序类,可用于安排将来某个特定时间执行的线程。 Java Timer类可用于安排任务一次运行或定期运行。
java.util.TimerTask是实现Runnable接口的抽象类 ,我们需要扩展该类以创建可以使用java Timer类进行调度的自己的TimerTask。
检查这篇文章中的Java Timer示例 。
A thread pool manages the pool of worker threads, it contains a queue that keeps tasks waiting to get executed.
A thread pool manages the collection of Runnable threads and worker threads execute Runnable from the queue.
java.util.concurrent.Executors provide implementation of java.util.concurrent.Executor interface to create the thread pool in java. Thread Pool Example program shows how to create and use Thread Pool in java. Or read ScheduledThreadPoolExecutor Example to know how to schedule tasks after certain delay.
线程池管理工作线程池,它包含一个使任务等待执行的队列。
线程池管理可运行线程的集合,工作线程从队列中执行可运行线程。
java.util.concurrent.Executors提供java.util.concurrent.Executor接口的实现,以在Java中创建线程池。 线程池示例程序显示了如何在Java中创建和使用线程池。 或阅读ScheduledThreadPoolExecutor示例以了解在某些延迟后如何安排任务。
Thread class run() method code is as shown below.
public void run() {
if (target != null) {
target.run();
}
}
Above target set in the init() method of Thread class and if we create an instance of Thread class as new TestThread()
, it’s set to null. So nothing will happen if we don’t override the run() method. Below is a simple example demonstrating this.
It will print only below output and terminate.
Before starting thread
After starting thread
线程类的run()方法代码如下所示。
在Thread类的init()方法中设置的目标上方,如果我们将Thread类的实例创建为new TestThread()
,则将其设置为null。 因此,如果不覆盖run()方法,将不会发生任何事情。 下面是一个简单的示例来说明这一点。
public class TestThread extends Thread {
//not overriding Thread.run() method
//main method, can be in other class too
public static void main(String args[]){
Thread t = new TestThread();
System.out.println("Before starting thread");
t.start();
System.out.println("After starting thread");
}
}
它将仅在输出下方打印并终止。
Atomic operations are performed in a single unit of task without interference from other operations. Atomic operations are necessity in multi-threaded environment to avoid data inconsistency.
int++ is not an atomic operation. So by the time one thread read its value and increment it by one, another thread has read the older value leading to the wrong result.
To solve this issue, we will have to make sure that increment operation on count is atomic, we can do that using Synchronization but Java 5 java.util.concurrent.atomic provides wrapper classes for int and long that can be used to achieve this atomically without the usage of Synchronization. Go to this article to learn more about atomic concurrent classes.
原子操作在单个任务单元中执行,而不受其他操作的干扰。 在多线程环境中,原子操作是必需的,以避免数据不一致。
int ++不是原子操作。 因此,当一个线程读取其值并将其递增1时,另一线程读取了较早的值,从而导致错误的结果。
为了解决这个问题,我们必须确保对count的增量操作是原子的,我们可以使用Synchronization做到这一点,但是Java 5 java.util.concurrent.atomic为int和long提供了包装器类,可用于自动实现此目的不使用同步。 转至本文以了解有关原子并发类的更多信息。
Lock interface provides more extensive locking operations than can be obtained using synchronized methods and statements. They allow more flexible structuring, may have quite different properties and may support multiple associated Condition objects.
The advantages of a lock are
Read more at Java Lock Example.
与使用同步方法和语句相比,锁定接口提供了更广泛的锁定操作。 它们允许更灵活的结构,可以具有完全不同的属性,并且可以支持多个关联的Condition对象。
锁的优点是
在Java Lock Example中阅读更多内容。
In Java 5, Executor framework was introduced with the java.util.concurrent.Executor interface.
The Executor framework is a framework for standardizing invocation, scheduling, execution, and control of asynchronous tasks according to a set of execution policies.
Creating a lot many threads with no bounds to the maximum threshold can cause the application to run out of heap memory. So, creating a ThreadPool is a better solution as a finite number of threads can be pooled and reused. Executors framework facilitate the process of creating Thread pools in java. Check out this post to learn with example code to create thread pool using Executors framework.
在Java 5中,Executor框架是通过java.util.concurrent.Executor接口引入的。
Executor框架是用于根据一组执行策略来标准化异步任务的调用,调度,执行和控制的框架。
创建很多没有最大阈值限制的线程可能会导致应用程序用尽堆内存。 因此,创建ThreadPool是更好的解决方案,因为可以合并并重用有限数量的线程。 执行程序框架简化了用Java创建线程池的过程。 查看这篇文章,以学习使用Executors框架创建线程池的示例代码。
java.util.concurrent.BlockingQueue is a Queue that supports operations that wait for the queue to become non-empty when retrieving and removing an element, and wait for space to become available in the queue when adding an element.
BlockingQueue doesn’t accept null values and throw NullPointerException if you try to store null value in the queue.
BlockingQueue implementations are thread-safe. All queuing methods are atomic in nature and use internal locks or other forms of concurrency control.
BlockingQueue interface is part of the Java collections framework and it’s primarily used for implementing the producer-consumer problem.
Check this post for producer-consumer problem implementation using BlockingQueue.
java.util.concurrent.BlockingQueue是一个Queue,它支持以下操作:在检索和删除元素时等待队列变为非空,在添加元素时等待队列中的空间变为可用。
如果尝试在队列中存储null值,则BlockingQueue不接受null值并引发NullPointerException。
BlockingQueue实现是线程安全的。 所有排队方法本质上都是原子的,并使用内部锁或其他形式的并发控制。
BlockingQueue接口是Java集合框架的一部分,它主要用于实现生产者-消费者问题。
查看此帖子,了解使用BlockingQueue实现生产者-消费者问题 。
Java 5 introduced java.util.concurrent.Callable interface in concurrency package that is similar to Runnable interface but it can return any Object and able to throw Exception.
The Callable interface uses Generics to define the return type of Object. Executors class provide useful methods to execute Callable in a thread pool. Since callable tasks run in parallel, we have to wait for the returned Object. Callable tasks return java.util.concurrent.Future object. Using Future we can find out the status of the Callable task and get the returned Object. It provides the get() method that can wait for the Callable to finish and then return the result.
Check this post for Callable Future Example.
Java 5在并发包中引入了java.util.concurrent.Callable接口,该接口类似于Runnable接口,但是它可以返回任何Object并能够引发Exception。
Callable接口使用泛型定义Object的返回类型。 Executors类提供有用的方法来在线程池中执行Callable。 由于可调用任务并行运行,因此我们必须等待返回的Object。 可调用任务返回java.util.concurrent.Future对象。 使用Future,我们可以找出Callable任务的状态并获取返回的Object。 它提供了get()方法,可以等待Callable完成,然后返回结果。
检查此帖子以获取Callable Future Example 。
FutureTask is the base implementation class of Future interface and we can use it with Executors for asynchronous processing. Most of the time we don’t need to use FutureTask class but it comes real handy if we want to override some of the methods of Future interface and want to keep most of the base implementation. We can just extend this class and override the methods according to our requirements. Check out Java FutureTask Example post to learn how to use it and what are different methods it has.
FutureTask是Future接口的基本实现类,我们可以将其与Executors一起使用以进行异步处理。 在大多数情况下,我们不需要使用FutureTask类,但是如果我们要覆盖Future接口的某些方法并希望保留大多数基本实现,它将非常方便。 我们可以扩展此类并根据我们的要求覆盖方法。 查看Java FutureTask示例帖子,了解如何使用它以及它有哪些不同的方法。
Java Collection classes are fail-fast which means that if the Collection will be changed while some thread is traversing over it using iterator, the iterator.next() will throw ConcurrentModificationException.
Concurrent Collection classes support full concurrency of retrievals and adjustable expected concurrency for updates.
Major classes are ConcurrentHashMap, CopyOnWriteArrayList and CopyOnWriteArraySet, check this post to learn how to avoid ConcurrentModificationException when using iterator.
Java Collection类是快速失败的,这意味着,如果在使用迭代器遍历某个线程时更改Collection,则iterator.next()将抛出ConcurrentModificationException。
并发集合类支持检索的完全并发性和可调整的预期更新并发性。
主要类是ConcurrentHashMap,CopyOnWriteArrayList和CopyOnWriteArraySet,请查看此文章以了解如何在使用Iterator时避免ConcurrentModificationException 。
Executors class provide utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes.
Executors class can be used to easily create Thread Pool in java, also this is the only class supporting execution of Callable implementations.
Executors类为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供实用程序方法。
Executors类可用于在Java中轻松创建线程池,这也是唯一支持执行Callable实现的类。
Some important concurrent API enhancements are:
Recommended Read: Java 8 Features
一些重要的并发API增强功能包括:
推荐阅读 : Java 8功能
That’s all for Java Thread and Concurrency interview questions, I have been adding more to this list. So bookmark the post for future reference.
这就是Java线程和并发面试问题的全部,我一直在此列表中添加更多内容。 因此,将帖子添加为书签以供将来参考。
翻译自: https://www.journaldev.com/1162/java-multithreading-concurrency-interview-questions-answers
java并发多线程面试