The previous article talked about the creation of threads and some common methods, but in use, most use thread pools to manage the creation, running, destruction and other processes of threads. This article will focus on the basics of thread pools, including creating threads through thread pools, basic information about thread pools, and so on.

Create a thread

preparation

All the code in this section is on the CreateThreadByPool class, which also has an inner class, MyThread, that implements the Runnable interface.

First, write out the basic code

public class CreateThreadByPool {
    public static void main(String[] args) {}}class MyThread implements Runnable {

    @Override
    public void run(a) {
        System.out.println(Thread.currentThread().getName() + " processing");
        process();
        System.out.println(Thread.currentThread().getName() + " end");
    }

    private void process(a) {
        try {
            Thread.sleep(3000);
        } catch(InterruptedException e) { e.printStackTrace(); }}@Override
    public String toString(a) {
        return String.format("MyThread{%s}", Thread.currentThread().getName()); }}Copy the code

Just to recap, what is the common way to code when we want to create 10 threads

private static void createThreadByNormalWay(a) {
    for (int i = 0; i < 10; i++) {
        MyThread myThread = new MyThread();
        Thread thread = newThread(myThread); thread.start(); }}Copy the code

In the code you can see, you start the thread directly by using start(), but what about using a thread pool

Through the Executors

The first way to create a thread pool is by using the Static method like Executors. You can create a total of 4 thread pools by doing so.

As you can see, the return is ExecutorService, so you need to accept the return value, and finally start the thread with execute

private static void createThreadByPool(a) {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    for (int i = 0; i < 10; i++) {
        MyThread myThread = newMyThread(); executorService.execute(myThread); }}Copy the code

Regardless of the underlying implementation, at least in code threads are handed over to a thread pool for execution, so that threads can be managed uniformly.

The simple analogy is that the former is to ask you to go to the monitor to sign in, while the latter is the monitor to manage the whole class. What’s the difference between a normal method and a thread created from a thread pool

The following differences can be clearly seen

  • Threads have different names
  • And the normal way is to create 10 threads, while the latter only created 5 threads (which we set ourselves).
  • The former is basically 10 threads are processed at the same time, the latter is a maximum of 5 threads, need to wait for the completion of the thread is free to process other threads.

Through the ThreadPoolExecutor

In addition to using Executors. NewFixedThreadPool () to create a thread pool, you can also through new ThreadPoolExecutor (), there may be confused by some friend, how the class is the ExecutorService back above, Now ThreadPoolExecutor is returned, which is the same thing.

ThreadExecutorPool inherits AbstractExecutorService, which implements ExecutorService. The code for the thread pool created by this method is as follows

You can run it this way and see what the different parameters in the constructor mean in a later section and come back to it later.

private static void createThreadByThreadPoolExecutor(a) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5.5.0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
    for (int i = 0; i < 10; i++) {
        MyThread myThread = newMyThread(); executor.execute(myThread); }}Copy the code

Let’s look at the results

The output is nothing to write home about, but as you will have noticed in the last GIF, starting a thread from the thread pool does not exit the application, it keeps running. This is because we have no shutdown thread pool.

The difference between

Back up to see Executors. Static method this method to create thread pool source

The new ThreadPoolExecutor() constructor has fewer parameters than the ThreadPoolExecutor() constructor. The new ThreadPoolExecutor() constructor has fewer parameters than the ThreadPoolExecutor() constructor.

Songshan Version of Ali Java Development Manual clearly stipulates two points: first, thread resources must be provided through thread pool, and explicit thread creation is not allowed; * Thread pools are not allowed to be created by Executors, but by ThreadPoolExecutor.

Focusing on the second point of forcing threads to be created using ThreadPoolExecutor for the same reasons below, take a look at the source code for FixedThreadPool and SingleThreadPool

Otherwise, you can see that the queue in both call constructors is LinkedBlockingQueue. This queue is unbounded, so with the allowed request length integer.max_value, it will pile up a lot of requests, resulting in OOM.

Take a look at the source code for CachedThreadPool

Note that the second argument to the constructor is the maximum number of threads in the thread pool, which is set to integer.max_value, which may create a large number of threads, resulting in OOM.

Thread pool information

ThreadPoolExecutor

As you can see above, the most important and appropriate method for creating a thread pool is new ThreadPoolExecutor(). Let’s focus on the ThreadPoolExecutor class

So this is all the properties in the class, and then let’s look at the constructors

There are four, but in the end there’s only one constructor, so let’s talk about what these parameters mean, and then you can go back to the example in the last section.

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
	// Omit the implementation
}
Copy the code
  • CorePoolSize: the number of core threads, in plain English, the number of working threads

  • MaximumPoolSize: The maximum number of threads that can be held in this thread pool

  • KeepAliveTime: keepAliveTime. When the number of threads in the thread pool is greater than the number of core threads, if no task is submitted, threads outside the core thread pool will not be destroyed immediately, but will wait until the waiting time exceeds this field before being reclaimed for destruction

  • Unit: Indicates the unit of survival time

  • WorkQueue: a workQueue that exists before a thread is called

  • ThreadFactory: a threadFactory used by an execution program to create a new thread

  • Handler: Rejection policy adopted when thread boundaries and queue capacity are reached

For this rejection strategy, briefly, there are four implementations.

Implement the RejectedExecutionHandler interface to implement its own rejection policy

Monitoring thread

Let’s simply implement a rejection policy of our own and look at the attributes in the above class

First you need a monitor thread class

class MonitorThread implements Runnable {
	
    // Inject a thread pool
    private ThreadPoolExecutor executor;

    public MonitorThread(ThreadPoolExecutor executor) {
        this.executor = executor;
    }

    private boolean monitor = true;

    public void stopMonitor(a) {
        monitor = false;
    }

    @Override
    public void run(a) {
        // Monitor always running, output status every 3s
        while (monitor) {
            // The main logic is to monitor the state of the thread pool
            System.out.println(
                    String.format("[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s, rejectedExecutionHandler: %s".this.executor.getPoolSize(),
                            this.executor.getCorePoolSize(),
                            this.executor.getActiveCount(),
                            this.executor.getCompletedTaskCount(),
                            this.executor.getTaskCount(),
                            this.executor.isShutdown(),
                            this.executor.isTerminated(),
                            this.executor.getRejectedExecutionHandler()));

            try {
                Thread.sleep(3000);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

At the same time, a customized rejection policy is implemented

It’s still not processing r, it’s rejected, it’s just printed out, but it’s not actually processed

class MyRejectedExecutionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        System.out.println("task is rejected"); }}Copy the code

Next comes the public class TheradPoolInfo, noting that the worker thread uses the MyThread class from the previous section

public class ThreadPoolInfo {
    public static void main(String[] args) throws InterruptedException {
        // create a new thread pool with 3 core threads and a maximum of 5,30 seconds
        // The queue is ArrayBlockingQueue, and the size boundary is 3, and the policy custom output is rejected in one sentence
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3.5.30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), new MyRejectedExecutionHandler());
        
        // Start the monitoring thread
        MonitorThread monitorThread = new MonitorThread(executor);
        new Thread(monitorThread).start();
        
        // Start the worker thread
        for (int i = 0; i < 10; i++) {
            executor.execute(new MyThread());
        }
		
        // Close the thread pool and monitor threads
        Thread.sleep(12000);
        executor.shutdown();
        Thread.sleep(3000); monitorThread.stopMonitor(); }}Copy the code

Expected result: As you can see from the constructor, it is expected that three core threads will execute the task, two threads will be rejected, and eight tasks will be completed (maximum number of threads is 5, queue length is 3, more on that in the next article).

You can see that the results are as expected

Creation is not easy, if it is helpful to you, welcome to like, collect and share!