[Interview with experts] - Java multi-threading (17 questions)

1. What is multithreading?

A thread is the smallest unit that the operating system can perform operation scheduling. A thread is a sequential flow of control within a program.

2. What is the difference between threads and processes?

Processes contain threads. There can be many threads in a process, and each thread performs different tasks. For example, if you open NetEase Cloud and listen to songs while downloading songs, then the entire NetEase Cloud application is considered a process, and playing music is a thread under the process, and downloading songs is another thread under the process.
Threads are smaller units of execution than processes.

3. Why do we use threads? What are the advantages and disadvantages of threads?

  • advantage
    • Make program execution more efficient. Enables tasks to be executed in parallel.
    • Make fuller use of CPU resources. Better utilization of system resources. In a multi-threaded program, when one thread must wait, the CPU can run other threads instead of waiting, greatly improving the efficiency of the program.
    • Increased program applicability and flexibility. Since all threads of the same process share the same memory, there is no need for a special data transfer mechanism, and there is no need to establish a shared storage area or shared file, thereby enabling coordinated operations and operations between different tasks, data interaction, resource allocation, etc. Problems are easier to solve.
    • Improve user experience. For example, those more time-consuming tasks can be executed in a separate thread in the background, while the user can do other things.
  • shortcoming
    • Multithreading consumes more memory space.
    • Thread switching requires resources.
    • Multi-threading can easily cause deadlocks and data insecurity.

4. What are the methods to create threads?

  • Inherit Thread and override the run method.
    • Features: (1) Unable to obtain the sub-thread return value. (2) The run method cannot throw exceptions.
class MyThread extends Thread {
    
    
	@Override
    public void run() {
    
    
        // 线程执行的代码
    }
}

// 创建并启动线程
MyThread thread = new MyThread();
thread.start();

  • Implement the Runnable interface.
    • Features: (1) Unable to obtain the sub-thread return value. (2) The run method cannot throw exceptions.
class MyRunnable implements Runnable {
    
    
    public void run() {
    
    
        // 线程执行的代码
    }
}

// 创建并启动线程
Thread thread = new Thread(new MyRunnable());
thread.start();
  • Implement the Callable interface.
    • Features: (1) The return value of the sub-thread can be obtained. (2) The run method can throw exceptions.

5. What are the statuses of threads?

  • New state (NEW): A thread is created when new Thread() is executed. Once the thread object is created, it will be in a new state.
  • Running state (RUNNABLE): After executing Start(), the thread is in the ready state, but will not execute immediately and seize the CUP time slice.
  • BLOCKED: The state of a thread waiting for a monitor lock. If there are two threads A and B executing in parallel in the same JVM, and after A first enters the synchronized block or method, thread B will be in a blocked state at this time.
  • Waiting state (WAITING):
  • Time-limited waiting (TIMED_WAITING): The thread calls the Thread.sleep(millis) method.
  • Termination status (TERMINATED/'tɜːrmə,net/): The thread execution is completed.
    Insert image description here

6. Thread priority?

The priority of threads is divided into 10 levels in total, and the default priority of threads is 5.

  • In the polling mechanism with time slices, the probability of a "high priority" thread being assigned a time slice by the CPU is greater than that of a "low priority" thread. But it is not necessarily the "high priority" thread that will execute first.
  • In the non-time slice polling mechanism, "high priority" threads will be executed first. If there is a "low priority" thread running and a "high priority" thread is in a runnable state, the "low priority" thread will be executed after the "low priority" thread is executed.
class ThreadTest extends Thread{
    
    
    public void run(){
    
    
        ...
    }
}
...
 Thread t1=new ThreadTest("thread1");    // t1线程
 Thread t2=new ThreadTest("thread2");    // t2线程
 t1.setPriority(1);  // 设置t1的优先级为1
t2.setPriority(5);  // 设置t2的优先级为5

7. Commonly used methods of threads and their functions?

  • threadTest.run(): The execution body of the thread, generally needs to be rewritten. The content in the execution body is the content executed by the current thread.
  • threadTest.start(): Method to start a thread. If this method is called, the thread will be in a ready state.
  • Object.wait(): Puts the current thread in a waiting (blocked) state and releases the lock on the object held. When using this method of a thread, you need to handle the Interrupted exception. The wait() method waits until the lock is acquired or interrupted. wait(long timeout) sets a timeout interval and returns if the lock is not obtained within the specified time.
    • After calling this method, the current thread enters sleep state until the following events occur.
    • Other threads called the object's notify method.
    • Other threads called the object's notifyAll method.
    • Other threads call interrupt to interrupt this thread.
    • The time interval has arrived.
  • threadTest.suspend(): Suspend the thread and resume() must be called by other threads.
  • Thread.join(): The join() method adds the specified thread to the current thread, and can merge two alternately executing threads into sequential execution threads. For example, if the join() method of thread A is called in thread B, thread B will not be executed until thread A completes execution. The main function of join is to let the main thread wait for the child thread to finish before it can continue running. The bottom layer of join is to call the wait() method. The wait() method releases the lock resource after being called, so the join method also releases the lock resource when it is called. Using the join() method also requires handling the Interrupted exception.
  • Thread.yield(): Pauses the currently running thread object, gives up the current CPU resources, and executes other threads. Return the currently running thread to a runnable state and allow other threads with the same priority as the current thread to get the opportunity to run. Therefore, another function of yield() is to allow other threads with the same priority or higher than the current priority to get the chance to run. Level threads execute in turns. However, after calling the yield() method, you cannot necessarily give in, because the yielding thread may be selected again by other thread schedulers, so the yielding effect may not be achieved.
  • Thread.sleep(): sleep() is a static local method. Let the thread sleep. During this period, the thread does not consume CPU resources. This method is very similar to the wait() method, but the only difference is that wait() will release the lock resource, while sleep() will not.
  • object.nodify(): Wake up a thread in the waiting state. Of course, when this method is called, it cannot exactly wake up a thread in the waiting state. Instead, the JVM determines which thread to wake up, and it has nothing to do with the priority;
  • object.nodifyAll(): Wake up all threads in the waiting state. This method does not give the object lock to all threads, but allows them to compete. Only the thread that obtains the lock can enter the ready state;
  • threadTest.setPriority(): Set the priority of the thread.
  • Thread.currentThread(): Outputs the current thread.

8.Have you ever used a thread pool? Why use thread pool?

We can create a thread pool through ThreadPoolExecutor.

If a large number of tasks need to be processed, frequently creating and destroying threads will waste time and efficiency, especially memory. In order to reuse threads and let them continue to perform other tasks instead of being destroyed immediately, thread pools came into being.

While thread pools are a powerful mechanism for building multi-threaded applications, using them is not without risks. Like other multi-threaded applications, applications built with thread pools are susceptible to concurrency risks such as synchronization errors and deadlocks. Additionally, thread pools are susceptible to specific risks, such as pool-related deadlocks, resource starvation, and thread leaks.

9.Java thread classification?


  • Ordinary threads created by user threads (User Thread) are all user threads.
  • Daemon Thread
    The so-called daemon thread is a thread that provides a general service in the background when the program is running. For example, garbage collection is a daemon thread. When all non-daemon threads end, the program will terminate and all daemon threads in the program will be killed.
  • Switch a user thread to a daemon thread.
    To switch a user thread to a daemon thread, use the Thread.setDaemon() method. But pay attention to the timing of switching, which must be set before the thread starts. Otherwise, an IllegalThreadStateException() exception will be reported because the running thread cannot be converted into a daemon thread.
  • The difference between user threads and daemon threads.
    The main difference between the two is the time when the JVM virtual machine leaves.
    User thread: When there is any user thread that has not left, the JVM will not leave.
    Daemon thread: If only the daemon thread is left, the JVM can leave.

10.What is deadlock?

When two or more processes or threads wait indefinitely for a condition that will never occur, and the system is in a stagnant state, this is a deadlock.

11. What are the causes and conditions for deadlock?

  • reason:
    • Insufficient system resources
    • The order in which processes are run and advanced is inappropriate
    • Improper allocation of resources
  • condition:
    • Mutually exclusive condition: A resource can only be used by one thread at a time.
    • Possess and wait: When a process acquires a share of resource A and then requests another share of resource B and is blocked, it will keep the acquired resource A.
    • No forcible possession: The resources that have been obtained by the process cannot be forcibly deprived of them before they are used up.
    • Loop waiting condition: A head-to-tail connection loop waiting resource relationship is formed between several threads.

12. How to prevent deadlock?

Prevent deadlock by setting certain restrictions to destroy one or more of the four conditions for deadlock. However, because the constraints imposed are often too harsh, system resource utilization and system throughput are reduced.

  • Break the mutual exclusion condition: cancel the mutual exclusion in the system. If the resource is not used exclusively by one process, then deadlock will definitely not occur. But generally speaking, among the four conditions listed, the "mutually exclusive" condition cannot be destroyed. Therefore, deadlock prevention mainly involves destroying several other necessary conditions, and does not involve destroying the "mutually exclusive" condition.
  • Break the possession and waiting condition: adopt a resource pre-allocation strategy, that is, apply for all resources before the process runs, and run if satisfied, otherwise wait.
    Before each process makes a new resource request, it must first release the resources it previously occupied.
  • Breaking the inalienability condition: When a process occupies certain resources and then further applies for other resources but cannot satisfy them, the process must release the resources it originally occupied.
  • Break the loop waiting condition: implement an orderly resource allocation strategy, number all resources in the system uniformly, and all processes can only apply for resources in the form of increasing sequence numbers.

13. Why is it not recommended to use Executor static project to create a thread pool?

The following are some reasons why it is not recommended to use Executors directly to create a thread pool:
1. Lack of fine control over the thread pool: Executors use the newSingleThreadExecutor single thread thread pool, that is, only one thread works in the thread pool at a time, and the single thread executes tasks serially. , thread expansion and reduction cannot be realized based on changes in request concurrency, and the number of threads is always specified.
2. May cause memory leaks: LinkedBlockingQueue unbounded queue is used, and the queue length parameter is not passed in, so the number of elements that can be supported inside the queue is Integer.MAX_VALUE, which can be regarded as an unbounded queue. If the request data is too much and the rejection policy will not be triggered, if the thread cannot consume it, the memory will be filled up over time, resulting in OOM.

14. How to create a thread pool?

Call the constructor of ThreadPoolExecutor to create the thread pool yourself. When creating, just specify the capacity for BlockQueue.

private static ExecutorService executor = new ThreadPoolExecutor(10,10,60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue(10));

15.What are the parameters for using ThreadPoolExecutor thread pool?

  • corePoolSize: the number of core threads, will always exist unless allowCoreThreadTimeOut is set to true
  • maximumPoolSize: The maximum number of thread pools allowed by the thread pool
  • keepAliveTime: The number of threads exceeds corePoolSize, the maximum timeout for idle threads
  • unit: unit of timeout time
  • workQueue: Work queue, saves unexecuted Runnable tasks
  • threadFactory: Factory class for creating threads
  • handler: rejection policy, will be called when the thread is full and the work queue is also full. Used to implement various rejection strategies.

16.What are the commonly used thread pools?

  • newSingleThreadExecutor
    Single thread thread pool, that is, only one thread works in the thread pool at a time, and a single thread executes tasks serially
  • newFixedThreadExecutor(n)
    A fixed number of thread pools. Each task submitted is a thread until the maximum number of thread pools is reached, and then it enters the waiting queue until the previous task is completed before continuing to execute.
  • newCacheThreadExecutor (recommended)
    can cache the thread pool. When the size of the thread pool exceeds the threads required to process the task, some idle threads (usually no execution for 60 seconds) will be recycled. When a task comes, it will be added intelligently. new thread to execute.
  • newScheduleThreadExecutor
    has an unlimited thread pool that supports scheduled and periodic execution threads.

17. Tell me about the execution process of ThreadPoolExecutor thread pool?

  • Creation of thread pool: First, create a ThreadPoolExecutor instance and configure the parameters of the thread pool, including the number of core threads, the maximum number of threads, thread survival time, task queue, etc. These parameters determine the behavior of the thread pool.

  • Task submission: Submit the task to the thread pool for execution. Tasks can be implementation classes of Runnable or Callable interfaces. The thread pool manages the execution of tasks.

  • Core thread processing tasks: If the number of threads in the current thread pool does not reach the number of core threads, a new core thread will be created to perform the task. Each task is assigned a thread to process.

  • Task queue storage tasks: If the number of threads in the thread pool has reached the number of core threads, but tasks continue to be submitted, then these tasks will be stored in the task queue waiting for execution. The task queue can be of LinkedBlockingQueue, ArrayBlockingQueue and other types, depending on the thread pool configuration.

  • Maximum thread processing tasks: If the task queue is full and the number of threads in the thread pool has not reached the maximum number of threads, the thread pool will create new threads to process tasks until the number of threads reaches the maximum number of threads.

  • Saturation strategy processing tasks: If the number of threads in the thread pool has reached the maximum number of threads and the task queue is full, the task will be processed according to the set saturation strategy. Saturation strategies can be dropping tasks, throwing exceptions, blocking, etc., depending on the configuration of the thread pool.

  • Task execution: Threads in the thread pool will take out tasks from the task queue and execute them. After execution is completed, the thread may be recycled (if the number of core threads is exceeded, according to the thread survival time and idle time policy), or continue to perform other tasks.

  • Task completion: After task execution is completed, the thread pool can return the results to the caller (for Callable tasks), or return nothing (for Runnable tasks).

  • Thread pool maintenance: The thread pool will automatically maintain the number of threads according to the configuration, such as recycling or creating threads based on thread survival time and idle time policies.

  • Shut down the thread pool: When the thread pool is no longer needed, the shutdown() or shutdownNow() method should be called to close the thread pool and release resources.

Guess you like

Origin blog.csdn.net/qq_42785250/article/details/132990015