preface

Note: all tests in this article were performed in JDK1.8.

This article is I in the study of Java multithreading this knowledge, do some summary and record.

If there is an incorrect place, please bear with us and give directions, thank you!

Some of the content in this article is referenced and adopted from the following works:

www.cnblogs.com/vipstone/p/…

www.bilibili.com/video/BV1dt…

1. Basic Concepts

We know that the CPU executes code sequentially, so by nature a computer with a single CPU does not perform multiple tasks at the same time. But in reality, when we use computers, we can chat on wechat while listening to music. So how does that work? In fact, operating system multitasking is to allow the CPU to perform multiple tasks alternately.

Take, for example, a classroom with three groups of grade one, grade two, and grade three at the same time, and the teacher spends one minute teaching grade one, one minute teaching grade two, one minute teaching grade three, and so on in turn, it looks like teaching three grades at once.

Similarly, when we use computers, we listen to music while chatting on wechat. The CPU lets wechat execute for 0.001 seconds and the music player for 0.001 seconds. In our view, the CPU is performing multiple tasks at the same time.

1.1 Concepts of programs, processes, and threads

Program: An executable file that is stored on a disk or other data storage device, which is a bunch of static code.

Process: An executable program instance that runs in memory

Thread: A thread is an entity of a process and is the basic unit of CPU scheduling and dispatch.

Look at these concepts are not very abstract, look at very uncomfortable, so let me use examples to explain these concepts.

1.2 Running examples of the program

It says that we can listen to music while chatting on wechat when we use computers. So what’s the whole process of running this software?

  • If we want to use wechat to chat, most of us just double click the “wechat” shortcut on the desktop, and double click this shortcut to open a. Exe file, which is a program, please see the following picture:

  • Double-click the.exe file and load the executable into memory so that the CPU can read it easily. The executable instance is a process. See the picture below:

  • When we use wechat, wechat will do a lot of things, such as loading wechat UI interface, displaying wechat friend list and chatting with friends, which can be regarded as a single thread in the process of wechat.

Let me summarize the process with a picture:

Given the concept of threads, is there any question as to how threads are created? With that in mind, let’s look at how threads are created in Java.

Second, thread creation

2.1 Concept of Thread class

The java.lang.Thread class represents threads, and any Thread is an instance of the Thread class (subclass).

2.2 Common methods

A constructor Function is introduced
Thread() Constructs objects without arguments
Thread(String name) Constructs the object from the name specified by the argument
Thread(Runnable target) Constructs an object from the reference specified by the argument, where Runnable is an interface type
Thread(Runnable target, String name) An object is constructed by specifying a reference and name as parameters
Members of the method Function is introduced
run() 1. Construct a thread object using a Runnable reference, which ends up calling the version in the interface when called

2. The thread object is not constructed with a Runnable reference and does nothing when called
start() Used to start a thread, the Java virtual machine automatically calls the run method of that thread

2.3 Creation Methods

2.3.1 Creating a Custom Thread class

The custom class inherits The Thread class and overrides the run method as needed, then calls the start method on an object that creates the class in the main class, thus starting a Thread.

Example code is as follows:

// Create a custom SubThreadRun class that inherits Thread as an alternate Thread class
public class SubThreadRun extends Thread {
    @Override
    public void run(a) {
        // Prints integer values ranging from 1 to 20
        for (int i = 0; i < 20 ; i ++) {
            System.out.println("SubThreadRun thread:"+ i); }}}// Create the thread in the main method and start it
public class SubThreadRunTest {

    public static void main(String[] args) {

        //1. Declare that a reference to Thread refers to an object of subclass type
        Thread t1 = new SubThreadRun();
        // To start a thread, the Java virtual machine automatically calls the run method in that threadt1.start(); }} SubThreadRun:0In the SubThreadRun thread:2. In the SubThreadRun thread:19

Copy the code

Here you will have the following question, look at the example code:

public class SubThreadRunTest {

    public static void main(String[] args) {

        //1. Declare that a reference to Thread refers to an object of subclass type
        Thread t1 = new SubThreadRun();
        // Call the run method to testt1.run(); }} SubThreadRun:0In the SubThreadRun thread:1. In the SubThreadRun thread:19
Copy the code

Instead of calling start, we call run and find the same result. What is the difference between the two methods?

We also add a number to print 1-20 in the main method, and then use the run method and start method respectively to test, the example code is as follows:

// Use the run method to test
public class SubThreadRunTest {

    public static void main(String[] args) {

        //1. Declare that a reference to Thread refers to an object of subclass type
        Thread t1 = new SubThreadRun();
        //2. Call the run method to test
        t1.run();

        // Prints integer values ranging from 1 to 20
        for (int i = 0; i < 20 ; i ++) {
            System.out.println("-----mian-----"+ i); }}} SubThreadRun:0In the SubThreadRun thread:1.// Both are in the SubThreadRun threadIn the SubThreadRun thread:19-----mian-----0-----mian-----1.// both are -----mian----- methods-----mian-----19
     
    
// Use the start method to test
public class SubThreadRunTest {

    public static void main(String[] args) {

        //1. Declare that a reference to Thread refers to an object of subclass type
        Thread t1 = new SubThreadRun();
        // To start a thread, the Java virtual machine automatically calls the run method in that thread
        t1.start();

        // Prints integer values ranging from 1 to 20
        for (int i = 0; i < 20 ; i ++) {
            System.out.println("-----mian-----"+ i); }}} SubThreadRun:0-----mian-----0In the SubThreadRun thread:1In the SubThreadRun thread:2-----mian-----1.//SubThreadRun threads and mian methods interweaveIn the SubThreadRun thread:19-----mian-----19
Copy the code

From the above example we can see:

  • When a run test is called, it is essentially a call to a normal member method, so the execution flow is that the code in the run method is executed before it can proceed.
  • When you call the start test, you start another thread, plus the thread executing the main method. There are two threads running at the same time, so the output is staggered. (Now that I come to think of it, do you understand that I can use QQ Music to listen to songs while typing comments?)

We’ve already learned the first way to create threads. Are there any drawbacks to this way of creating threads? If the SubThreadRun class already inherits from a parent class, then we would use the class as a custom Thread class. To implement the SubThreadRun class using methods that inherit from Thread would violate the Java concept of non-multiple inheritance. So the second creation method avoids this problem.

2.3.2 Implement the Runnable interface

The custom class implements the Runnable interface and overwrites the run method, creates an object of the class as an argument to construct an object of type Thread, and then calls the start method with an object of type Thread.

Example code is as follows:

// Create a custom class SubRunnableRun that implements the Runnable interface
public class SubRunnableRun implements Runnable {
    @Override
    public void run(a) {
        for (int i = 0; i < 20 ; i ++) {
            System.out.println("SubRunnableRun thread:"+ i); }}}// Create the thread in the main method and start it
public class SunRunnableRunTest {

    public static void main(String[] args) {

        //1. Create an object of a custom type
        SubRunnableRun srr = new SubRunnableRun();
        //2. Construct an object of type Thread using this object as an argument
        Thread t1 = new Thread(srr);
        //3. Call the start method with an object of type Thread
        t1.start();

        // Prints all integers between 1 and 20
        for (int i = 0; i < 20 ; i ++) {
            System.out.println("-----mian-----"+ i); }}} SubRunnableRun thread:0-----mian-----0SubRunnableRun thread:1SubRunnableRun thread:2-----mian-----1.//SubRunnableRun threads and mian methods interweaveSubRunnableRun thread:19-----mian-----19
Copy the code

Is there a question at this point?

I instantiated Thread in the main method of the SunRunnableRunTest class. Why does this Thread call the run method of SubRunnableRun class that implements the Runnable interface instead of the run method of Thread class?

To solve this problem, we will enter the Thread class to look at the source code, the source code call process is as follows:

  1. As you can see from the SunRunnableRunTest class above, we create a Thread that calls the Thread’s parameter constructor and takes a parameter of type Runnable.

  1. Go into the Thread class and find the constructor with the argument, which calls the init method and passes the target argument as an argument.

  1. When we go to the corresponding init method, we find that the init method continues to call another overloaded init method, passing the target argument as an argument.

  1. Moving on to the overloaded init method, we see that target is assigned from the parameter to the member variable target.

  1. The run method in target is called as long as the target member variable of Thread exists.

By looking at the source code, we can see why the Thread class we created calls the Run method of the Runnable class.

2.3.3 Anonymous inner class to achieve creation

Each of the above ways of creating threads requires creating a separate class that inherits the Thread class or implements the Runnable interface and overwrites the Run method. Anonymous inner classes allow custom threads to be created without creating separate classes.

Example code is as follows:

public class ThreadNoNameTest {

    public static void main(String[] args) {

        // Anonymous inner class syntax format: parent/interface type reference variable = new parent/interface type {method override};
        //1. Create and start a thread using inheritance plus an anonymous inner class
        Thread t1 = new Thread() {
            @Override
            public void run(a) {
                System.out.println("Create Thread from Thread class..."); }}; t1.start();//2. Create and start a thread using an interface and an anonymous inner class
        Runnable ra = new Runnable() {
            @Override
            public void run(a) {
                System.out.println("Implement the Runnable interface to implement threads..."); }}; Thread t2 =newThread(ra); t2.start(); }}Copy the code

These two ways of creating threads using anonymous inner classes continue to simplify code, especially the way of creating threads using the Runnable interface, which can be simplified using Lambda expressions.

Example code is as follows:

public class ThreadNoNameTest {

    public static void main(String[] args) {

        //1. Create and start threads using inheritance and anonymous inner classes
        new Thread() {
            @Override
            public void run(a) {
                System.out.println("Create Thread from Thread class after simplifying...");
            }
        }.start();

        //2. Create and start a thread using a simplified interface and anonymous inner class
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                System.out.println("Simplified implementation of the Runnable interface to implement threads...");
            }
        }).start();
        
        //2-1. For interfaces with anonymous internal creation threads, we can continue to use lambda expressions for simplification.
        //Java8 starts to support lambda expressions :(parameter list) -> {method body; }
        Runnable ra = () -> System.out.println("Lambda expressions simplify implementation of Runnable interface approach implementation of threads...");
        new Thread(ra).start();
        
        // Continue simplifying
        new Thread(() -> System.out.println("Lambda expressions simplify implementation of Runnable interface approach implementation of threads...")).start(); }}Copy the code

2.3.4 Creating a vm using the Callable Interface

We have seen two ways to create a thread from the examples above, but the problem with both methods is that the run method does not return a value, so if we want to give a result after the thread ends, we need to implement the Callable interface to create a thread.

(1) Callable interface

Starting from the Java 5 new create a thread in the third way to implement Java. Util. Concurrent. Callable interface.

The common methods are as follows:

Members of the method Function is introduced
V call() Evaluates the result, and if it cannot, throws an exception

We know that the only way to start a Thread is to create a Thread class and call the start method. If we want to call the Call method in the Callable interface, we need to use the FutureTask class.

(2) FutureTask class

Java. Util. Concurrent. FutureTask class implements the RunnableFuture interface, RunnableFuture interface is a complex of Runnable and Future, a Future, FutureTask can perform asynchronous computation, You can check whether the asynchronous program is finished, and you can start and cancel the program, and get the final execution result of the program, and can also be used to get the return result after calling the method.

The common methods are as follows:

A constructor Function is introduced
FutureTask(Callable callable) Create a FutureTask that will execute the given Callable at runtime
Members of the method Function is introduced
V get() Gets the result of the call method calculation

The FutureTask class is a subclass of Runnable, so the FutureTask class can be used as an argument to Thread. This way we can create a thread and call the Call method in the Callable interface.

Example code is as follows:

public class ThreadCallableTest implements Callable {
    @Override
    public Object call(a) throws Exception {
        // Calculate the sum between 1 and 10000 and print it back
        int sum = 0;
        for (int i = 0; i <= 10000; i ++) {
            sum += i;
        }
        System.out.println("Result in call method:" + sum);
        return sum;
    }

    public static void main(String[] args) {

        ThreadCallableTest tct = new ThreadCallableTest();
        FutureTask ft = new FutureTask(tct);
        Thread t1 = new Thread(ft);
        t1.start();
        Object ob = null;
        try {
            ob = ft.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println("Result in main method:"+ ob); }} The result of the call method:50005000Results from the main method:50005000
Copy the code

2.3.5 Creating a thread Pool

The origin of thread pool:

Before talking about thread pool, let’s tell a story. A boss runs a restaurant, but this restaurant is very strange. Every time a customer comes, the boss will hire a new chef to cook. Under this way of doing business, the boss would be too busy hiring chefs to do anything.

As for the above story, the restaurant owners in real life are not so stupid. They all hire several chefs to wait in the kitchen before opening a restaurant. When customers come, they cook and serve dishes directly.

Let’s take a look at where thread pools come from: for example, in server programming, if a new worker thread is assigned to each client, and when the worker thread finishes communicating with the client, the thread is destroyed, this requires frequent creation and destruction of worker threads. If there are too many clients accessing the server, the performance of the server can be severely affected.

So how do we free up servers? By the way, just like the restaurant owner mentioned above, build a back kitchen and let the chef wait. In contrast to the server, a thread pool is created, and the thread waits for the client to connect. When the client finishes communicating, the server does not close the thread, but returns to the thread and waits. This frees up the server.

Thread pool concept:

When a server receives a client request, it takes a spare thread from the thread pool to serve it. Instead of closing the thread, it puts it back into the thread pool.

Related classes and methods:

  • There are seven ways to create a thread pool, but generally they fall into two categories:

  • Executors is a factory class for tools and thread pools. Use the following methods to create and return different types of thread pools:

    The return value methods Function is introduced
    static ExecutorService newFixedThreadPool(int nThreads) Create a thread pool of a fixed size, and if the number of tasks exceeds the number of threads, the excess portion will wait in the queue
    static ExecutorService newCachedThreadPool() Create a thread pool that can create new threads as needed. If the number of tasks at a time is greater than the number of threads, new threads can be created based on the number of tasks. If the task is completed, the thread is recycled after a period of caching.
    static ExecutorService newSingleThreadExecutor() Create a thread pool with a single number of threads to ensure first-in, first-out execution order
    static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) Create a thread pool that can execute deferred tasks
    static ScheduledExecutorService newSingleThreadScheduledExecutor() Create a single-threaded thread pool that can execute deferred tasks
    static ExecutorService newWorkStealingPool() Create a thread pool for preemptive execution (task execution order not determined)
  • ThreadPoolExecutor creates a thread pool using a constructor, which can be set to a maximum of seven parameters. The constructor is as follows:

    A constructor Function is introduced
    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) Create a thread pool the most primitive way
  • The ExecutorService interface is a real thread pool interface. The main implementation class is ThreadPoolExecutor. The common methods are as follows:

    Method statement Function is introduced
    void execute(Runnable command) Performs tasks and commands, usually used to perform Runnable
    Future submit(Callable task) Performs tasks and commands, typically for Callable execution
    void shutdown() Start orderly shutdown

Example code:

  1. Create a thread pool using the newFixedThreadPool method

    public class FixedThreadPool {
        public static void main(String[] args) {
            
            // Create a pool of two threads
            ExecutorService threadPool = Executors.newFixedThreadPool(2);
            // Create a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    System.out.println("Thread." + Thread.currentThread().getName() + "Mission accomplished!"); }};// The thread pool performs the taskthreadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); threadPool.execute(runnable); }} Thread: Pool -1-thread-2Mission carried out! Thread pool -1-thread-1Mission carried out! Thread pool -1-thread-2Mission carried out! Thread pool -1-thread-1Mission carried out!Copy the code

    As you can see from the results, these four tasks are executed by two fixed threads in the thread pool, and the thread pool does not create new threads to execute the task.

  2. Create a thread pool using the newCachedThreadPool method

    public class cachedThreadPool {
        public static void main(String[] args) {
    
            Create a thread pool
            ExecutorService executorService = Executors.newCachedThreadPool();
            //2. Set a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    System.out.println("Thread." + Thread.currentThread().getName() + "Mission accomplished!");
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                    }
                }
            };
            //3. Execute tasks
            for (int i = 0; i < 100; i ++) { executorService.execute(runnable); }}} Thread: Pool -1-thread-1Mission carried out! Thread pool -1-thread-4Mission carried out! Thread pool -1-thread-3Mission carried out! Thread pool -1-thread-2Mission carried out! Thread pool -1-thread-5Mission carried out! Thread pool -1-thread-7Mission carried out! Thread pool -1-thread-6Mission carried out! Thread pool -1-thread-8Mission carried out! Thread pool -1-thread-9Mission carried out! Thread pool -1-thread-10Mission carried out!Copy the code

    As you can see from the results, the thread pool creates the corresponding number of threads based on the number of tasks.

  3. Create a thread pool using the newSingleThreadExecutor method

    public class singleThreadExecutor {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            // Execute the task
            for (int i = 0; i < 10; i ++) {
                final int task = i + 1;
                executorService.execute(()->{
                    System.out.println("Thread." + Thread.currentThread().getName() + "Execute no." + task +"Mission!);
                    try {
                        Thread.sleep(1000);
                    } catch(InterruptedException e) { e.printStackTrace(); }}); }}} Thread: Pool -1-thread-1Perform the first1Task! Thread pool -1-thread-1Perform the first2Task! Thread pool -1-thread-1Perform the first3Task! Thread pool -1-thread-1Perform the first4Task! Thread pool -1-thread-1Perform the first5Task! Thread pool -1-thread-1Perform the first6Task! Thread pool -1-thread-1Perform the first7Task! Thread pool -1-thread-1Perform the first8Task! Thread pool -1-thread-1Perform the first9Task! Thread pool -1-thread-1Perform the first10Task!Copy the code

    As you can see from the results, the thread created by this method can guarantee the order of task execution.

  4. Create a thread pool using the newScheduledThreadPool method

    public class ScheduledThreadPool {
    
        public static void main(String[] args) {
    
            // Create a thread pool with 2 threads
            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
            // Record the current time when the task was created
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date startTime = new Date();
            String start = formatter.format(startTime);
            System.out.println("Time when task was created :" + start);
            // Create a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    Date endTime = new Date();
                    String end = formatter.format(endTime);
                    System.out.println("Thread." + Thread.currentThread().getName() + "Task execution time is :"+ end); }};// execute the task (parameters: runnable- task to execute, 2- time to delay execution from now on, timeunit.seconds - TimeUnit of delay parameter)
            for(int i = 0; i < 2; i ++) {
                scheduledExecutorService.schedule(runnable,2, TimeUnit.SECONDS); }}} Command output:2021-04-19 19:26:18Thread pool -1-thread-1The task execution time is:2021-04-19 19:26:20Thread pool -1-thread-2The task execution time is:2021-04-19 19:26:20
    Copy the code

    As you can see from the results, the thread pool created by this method can allocate existing threads to perform some tasks that need to be deferred.

  5. NewSingleThreadScheduledExecutor method is used to create a thread pool

    public class SingleThreadScheduledExecutor {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            // Create a task
            Date startTime = new Date();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String start = formatter.format(startTime);
            System.out.println("Task creation time :" + start);
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    Date endTime = new Date();
                    String end = formatter.format(endTime);
                    System.out.println("Thread." + Thread.currentThread().getName() + "Task execution time is :"+ end); }};// Execute the task
            for(int i = 0; i < 2; i ++) {
                scheduledExecutorService.schedule(runnable,2, TimeUnit.SECONDS); }}} Command output:2021-04-19 19:51:58Thread pool -1-thread-1The task execution time is:2021-04-19 19:52:00Thread pool -1-thread-1The task execution time is:2021-04-19 19:52:00
    Copy the code

    As you can see from the results, this method creates a thread pool with only one thread to perform tasks that need to be deferred.

  6. Create a thread pool using the newWorkStealingPool method

    public class newWorkStealingPool {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ExecutorService executorService = Executors.newWorkStealingPool();
            // Execute the task
            for (int i = 0; i < 4; i ++) {
                final int task = i + 1;
                executorService.execute(()->{
                    System.out.println("Thread." + Thread.currentThread().getName() + "Execute no." + task +"Mission!);
                });
            }
            // Make sure the task is executed
            while(! ExecutorService. IsTerminated () {}}} output: thread: ForkJoinPool -1-worker-9Perform the first1Task! Thread: ForkJoinPool -1-worker-4Perform the first4Task! Thread: ForkJoinPool -1-worker-11Perform the first3Task! Thread: ForkJoinPool -1-worker-2Perform the first2Task!Copy the code

    As you can see, this method creates a thread pool with enough threads to maintain the appropriate level of parallelism, and the task is preemptively executed. (Task execution order is uncertain)

  7. Create a thread pool using ThreadPoolExecutor

    Before writing the sample code, LET me give you an example of life (going to a bank) :

    Describe the business scenario: the bank has a total of 4 Windows, only 2 are open today, and then there are 3 positions in the waiting area. As shown below:

    • If there are less than or equal to five people dealing with the bank at the same time, then it is just right that two people deal with the bank first and the others wait in the waiting area. As shown below:

    • If the number of customers is six, the bank opens window number three. As shown below:

    • If the number of simultaneous transactions is seven, the bank will open window 4 for transactions. As shown below:

    • If more than seven people are doing business at the same time, the manager of the bank lobby will tell the people in the back that the branch is full and please go to another branch.

    Now let’s look at our ThreadPoolExecutor constructor, which can set up to seven parameters:

    ThreadPoolExecutor(int corePoolSize, 
    				   int maximumPoolSize, 
                       long keepAliveTime, 
                       TimeUnit unit, 
                       BlockingQueue<Runnable> workQueue, 
                       ThreadFactory threadFactory, 
                       RejectedExecutionHandler handler)
    Copy the code

    Parameter Description:

    1. CorePoolSize: number of core threads, threads that always exist in the thread pool (corresponding to the banking business model: open window from the start)
    2. MaximumPoolSize: the maximum number of threads that can be created in a thread pool, and several threads other than the core threads will be created when the pool’s task queue is full (for banking model: all Windows)
    3. KeepAliveTime: The maximum thread lifetime. When there is no work for a long time, the thread pool destroys some threads and keeps the core thread
    4. unit: the unit of time, which is the unit of the third parameter, which is combined into the maximum thread lifetime
      • TimeUnit. DAYS: day
      • TimeUnit. HOURS: HOURS
      • TimeUnit. MINUTES: points
      • TimeUnit. SECONDS: SECONDS
      • TimeUnit. MILLISECONDS: ms
      • TimeUnit. MICROSECONDS: MICROSECONDS
      • TimeUnit. NANOSECONDS: NANOSECONDS
    5. workQueue: wait queue, used to hold tasks waiting in the thread pool (corresponding to the banking business model: wait area)
      • ArrayBlockingQueue: a bounded blocking queue supported by arrays.
      • LinkedBlockingQueue: A bounded blocking queue of linked lists.
      • SynchronousQueue: This blocking queue does not store tasks but submits them directly to threads, resulting in the execution of submitted tasks by idle threads if available, or by creating a new thread otherwise.
      • PriorityBlockingQueue: An unbounded queue with a priority that returns the element with the highest or lowest priority each time it exits the queue
      • DelayQueue: an unbounded blocking queue that supports delayed acquisition of elements using a priority queue. Elements can only be extracted from the queue when the delay expires.
      • LinkedTransferQueue: An unbounded blocking queue consisting of a linked list structure.
      • LinkedBlockingDeque: A bidirectional blocking queue consisting of a linked list structure.
    6. ThreadFactory: threadFactory, used to create threads.
    7. handler: Reject policy. This policy is used to reject processing a task when the task exceeds the acceptable range of the thread pool.
      • ThreadPoolExecutor. AbortPolicy: when a task is added to the thread pool is refused, it will throw RejectedExecutionException anomalies (defaults to using this strategy)
      • ThreadPoolExecutor. CallerRunsPolicy: when a task is added to the thread pool is rejected, is called the place of the current thread pool threads to carry out the tasks of being rejected
      • ThreadPoolExecutor. DiscardOldestPolicy: when a task is added to the thread pool is rejected, will discard the old task in task queue is the first to join the queue, and then add the new task
      • ThreadPoolExecutor DiscardPolicy: if the task is rejected, this directly to ignore or abandon the task

    When the number of tasks is less than or equal to the sum of the number of core threads + the number of waiting queues:

    public class ThreadPoolExecutorTest {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2.4.100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
            // Create a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    System.out.println(Thread.currentThread().getName() + "==> Perform the task"); }};// Execute the task
            for (int i = 0; i < 5; i++) {
                threadPool.execute(runnable);
            }
            // Close the thread poolthreadPool.shutdown(); }} The following output is displayed: pool-1-thread-2==> Execute task Pool -1-thread-1==> Execute task Pool -1-thread-2==> Execute task Pool -1-thread-1==> Execute task Pool -1-thread-2==> Perform tasksCopy the code

    As you can see from the results, there are only two core threads executing tasks.

    When the number of tasks is greater than the total number of core threads + the number of wait queues, but less than or equal to the maximum number of threads:

    public class ThreadPoolExecutorTest {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2.4.100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
            // Create a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    System.out.println(Thread.currentThread().getName() + "==> Perform the task"); }};// Execute the task
            for (int i = 0; i < 7; i++) {
                threadPool.execute(runnable);
            }
            // Close the thread poolthreadPool.shutdown(); }} The following output is displayed: pool-1-thread-1==> Execute task Pool -1-thread-4==> Execute task Pool -1-thread-2==> Execute task Pool -1-thread-2==> Execute task Pool -1-thread-3==> Execute task Pool -1-thread-4==> Execute task Pool -1-thread-1==> Perform tasksCopy the code

    As you can see from the results, the maximum thread is started to execute the task.

    When the number of tasks is greater than the maximum number of threads:

    public class ThreadPoolExecutorTest {
    
        public static void main(String[] args) {
    
            // Create a thread pool
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2.4.100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
            // Create a task
            Runnable runnable = new Runnable() {
                @Override
                public void run(a) {
                    System.out.println(Thread.currentThread().getName() + "==> Perform the task"); }};// Execute the task
            for (int i = 0; i < 8; i++) {
                threadPool.execute(runnable);
            }
            // Close the thread poolthreadPool.shutdown(); }} Command output: Exception in thread"main" java.util.concurrent.RejectedExecutionException: Task com.zck.task18.ThreadPool.ThreadPoolExecutorTest$1@7f31245a rejected from java.util.concurrent.ThreadPoolExecutor@6d6f6e28[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 7]
    	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    	at com.zck.task18.ThreadPool.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:25)
        pool-1-thread-1==> Execute task Pool -1-thread-4==> Execute task Pool -1-thread-4==> Execute task Pool -1-thread-4==> Execute task Pool -1-thread-2==> Execute task Pool -1-thread-3==> Execute task Pool -1-thread-1==> Perform tasksCopy the code

    As you can see from the result, the task is larger than the maximum number of threads, and an exception is thrown directly using a reject policy.

Third, summary

This article describes three ways to create threads:

  • The custom class inherits the Thread class and overwrites the run method
  • Custom classes implement the Runnable interface and override the run method creation
  • Implement Callable interface creation

There are seven ways to create thread pools:

  • Create a thread pool using the newFixedThreadPool method
  • Create a thread pool using the newCachedThreadPool method
  • Create a thread pool using the newSingleThreadExecutor method
  • Create a thread pool using the newScheduledThreadPool method
  • NewSingleThreadScheduledExecutor method is used to create a thread pool
  • Create a thread pool using the newWorkStealingPool method
  • Create a thread pool using ThreadPoolExecutor