[in simple terms, Java multithreading, mybatis interview questions and answers

Posted by Goose87 on Wed, 24 Nov 2021 06:36:28 +0100

ExecutorService executorService = Executors.newFixedThreadPool(threadPoolNum, r -> new Thread(r, threadName));



The underlying layer of Executors uses ThreadPoolExecutor. We can understand some behaviors of ThreadPoolExecutor through ThreadPoolExecutor constructor.

ThreadPoolExecutor(

  int corePoolSize,

  int maximumPoolSize,

  long keepAliveTime,

  TimeUnit unit,

  BlockingQueue<Runnable> workQueue,

  ThreadFactory threadFactory,

  RejectedExecutionHandler handler)



Constructor parameter description

corePoolSize: indicates the minimum number of threads in the thread pool.

maximumPoolSize: indicates the maximum number of threads created by the thread pool.

Keepalivetime & unit: if a thread has been idle for keepalivetime & unit for so long and the number of threads in the thread pool is greater than corePoolSize, the idle thread will be recycled.

workQueue: work queue, which is synonymous with the work queue in the above example code.

threadFactory: through this parameter, you can customize how to create a thread. For example, you can specify a meaningful name for the thread.

Handler: you can customize the rejection policy of the task through this parameter. If all threads in the thread pool are busy and the work queue is full (provided that the work queue is a bounded queue), the thread pool will refuse to receive the task when it is submitted. As for the rejection policy, you can specify it through the handler parameter. ThreadPoolExecutor has provided the following four policies.

  • CallerRunsPolicy: the thread submitting the task executes the task itself.

  • AbortPolicy: the default rejection policy, which throws RejectedExecutionException.

  • DiscardPolicy: directly discard the task without any exception thrown.

  • Discard oldest policy: discarding the oldest task is actually discarding the task that entered the work queue first, and then adding the new task to the work queue.

Default working behavior of thread pool

The corePoolSize threads will not be initialized, and the worker thread will not be created until a task comes;

When the core thread is full, the thread pool will not be expanded immediately, but the tasks will be stacked in the work queue;

When the work queue is full, expand the thread pool until the number of threads reaches maximumPoolSize; (if the thread pool has not been expanded to the maximum number of threads, but the work queue has overflowed, the overflow request will be rejected)

If a task comes in after the queue is full and the maximum thread is reached, it shall be handled according to the rejection policy;

When the number of threads is greater than the number of core threads, if the thread still has no tasks to process after waiting for keepAliveTime, shrink the thread to the number of core threads.

ForkJoinPool

Fork/Join is a parallel computing framework, which is mainly used to support the divide and conquer task model. Fork in this computing framework corresponds to task decomposition in the divide and conquer task model, and Join corresponds to result merging.

The Fork/Join computing framework mainly includes two parts: one is the ForkJoinPool of divide and conquer tasks, and the other is the ForkJoinTask of divide and conquer tasks. The relationship between these two parts is similar to the relationship between ThreadPoolExecutor and Runnable. Both can be understood as submitting tasks to the thread pool, except that divide and conquer tasks have their own unique type ForkJoinTask.

ForkJoinPool is mainly suitable for computing intensive tasks. ForkJoinPool is used at the bottom of parallel stream in Java.

Here is a simple example of using ForkJoinPool:

  public static void main(String[] args) {

        ForkJoinPool forkJoinPool = new ForkJoinPool(4);



        Fibonacci fibonacci = new Fibonacci(5);

        Integer res = forkJoinPool.invoke(fibonacci);



        System.out.println(res);

    }



    static class Fibonacci extends RecursiveTask<Integer>{

        final int n;

        Fibonacci(int n){

            this.n = n;

        }

        @Override

        protected Integer compute() {

            if(n<=1){

                return n;

            }

            Fibonacci f1 = new Fibonacci(n-1);

            f1.fork();

            Fibonacci f2 = new Fibonacci(n-2);

            return f2.compute() + f1.join();

        }

    }



FutureTask

We can get the thread execution results through FutureTask (implementation class of Future interface). The main methods of FutureTask are as follows:

// Cancel task

boolean cancel(

  boolean mayInterruptIfRunning);

// Determine whether the task has been cancelled  

boolean isCancelled();

// Determine whether the task has ended

boolean isDone();

// Obtain task execution results

get();

// Obtain task execution results and support timeout

get(long timeout, TimeUnit unit);



The two get() methods are blocking. If the task is not completed when it is called, the thread calling the get() method will block and will not wake up until the task is completed.

ExecutorService executorService = Executors.newFixedThreadPool(10);

        Future<Integer> future = executorService.submit(() -> {

            return 1 + 1;

        });

        Integer res = future.get();

        System.out.println(res);

        Integer res2 = future.get(1000, TimeUnit.SECONDS);

        System.out.println(res2);



FutureTask implements Runnable and Future interfaces. Due to the implementation of Runnable interface, FutureTask object can be submitted to ThreadPoolExecutor as a task for execution.

// Create FutureTask

FutureTask<Integer> futureTask

  = new FutureTask<>(()-> 1+2);

// Create thread pool

ExecutorService es = 

  Executors.newCachedThreadPool();

// Submit FutureTask 

es.submit(futureTask);

// Get calculation results

Integer result = futureTask.get();



Thread count analysis

Multithreading can improve the response speed and throughput of the program. The number of threads created will have a great impact on the actual effect. Too few threads will waste CPU resources. Too many threads will lead to frequent thread switching, and the system performance will decline.

According to different program types, we can divide our programs into IO intensive and CPU intensive. The methods of calculating the optimal number of threads are different between the two programs.

last

CodeChina open source project: [analysis of Java interview questions of front-line large manufacturers + core summary learning notes + latest explanation Video]

Learning video:

Real interview questions for large factories:

It will waste CPU resources. Too many threads will lead to frequent thread switching, and the system performance will decline.

According to different program types, we can divide our programs into IO intensive and CPU intensive. The methods of calculating the optimal number of threads are different between the two programs.

last

CodeChina open source project: [analysis of Java interview questions of front-line large manufacturers + core summary learning notes + latest explanation Video]

Learning video:

[external chain picture transferring... (img-GYv1vOBK-1631086679294)]

Real interview questions for large factories:

Topics: Java Back-end Interview Programmer