Detailed explanation of Java threads and the use and principle of multi-thread thread pool

1. Java threads

This article mainly introduces the basic concepts and creation methods of threads and the detailed explanation and use of multi-threaded thread pools

1.1 What is a thread

To understand threads, we first need to know what a process is and the relationship between a process and a thread

  • Process
    A process is an independent unit for resource allocation and scheduling by the operating system. Every running program in the system is a process, and each process has its own independent memory space.
  • Thread
    A thread is the smallest unit for operation scheduling by the operating system. It is contained in a process, and each process can have multiple threads at the same time.

1.2 Thread Creation

There are three ways to create a thread: inherit the Thread class, implement the Runnable interface, and implement the Callable interface

  • Inherit Thread, rewrite the run method
public class TestThread extends Thread{
    
    
    private int count=0;
    @Override
    public void run() {
    
    
        //需要线程执行的逻辑
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
    }
    public static void main(String[] args) {
    
    
        TestThread testThread = new TestThread();
        //启动线程
        testThread.start();
    }
}
  • Implement the Runnable interface and rewrite the run method
public class TestThread1 implements Runnable{
    
    
    private int count=0;
    @Override
    public void run() {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
    }
    public static void main(String[] args) {
    
    
        TestThread1 testThread1 = new TestThread1();
        //把实现了runable接口的实现类对象作为参数,放到thread的构造方法
        Thread thread = new Thread(testThread1);
        //启动线程
        thread.start();
    }
}
  • Implement the Callable interface and rewrite the call method
//Callable接口与Runnable接口不同的是Callable接口的call方法是具有返回值的,
//如果有需要线程具有返回数据的情况使用callable
public class TestThread2 implements Callable {
    
    
    private int count = 0;
    @Override
    public Object call() throws Exception {
    
    
        for (int i = 0; i < 10; i++) {
    
    
            System.out.println(count++);
        }
        return count;
    }
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        TestThread2 testThread2 = new TestThread2();
        FutureTask task = new FutureTask(testThread2);
        Thread thread = new Thread(task);
        thread.start();
        Object o = task.get();
        System.out.println("count值为:"+o);

    }
}

1.3 Thread state

Threads have five states: new, ready, running, blocked, and dead.

  • New: When we new Thread () object, the thread object is in the new state
  • Ready: When we call the start() method, the thread enters the ready state, and the thread at this time enters the state of trying to obtain cpu resources.
  • Running: When the ready thread obtains the cpu execution right, the thread enters the running state.
  • Death: When the normal execution of the thread ends or the stop method is called, the thread enters the death state.
  • Blocking: When a thread sleeps, waits, or is blocked due to acquiring a lock, it enters the blocked state.
    When the sleep method of the Thread class is called, the thread enters the sleep state, and enters the ready state after sleeping for a specified time. When the
    yield and other methods of the Thread class are called, the thread will let other threads execute, and the thread enters the waiting state

Two, multi-threaded thread pool

  • Why use a thread pool?
    In business scenarios that require a large number of threads, we need to create threads frequently. When the threads are executed, they need to be destroyed. Whenever there is a new task, we need to recreate the threads. Such frequent creation and destruction of threads will take up a lot of system overhead.
  • What are the advantages of using a thread pool?
    Reduce resource consumption, and reduce the consumption caused by thread creation and destruction by reusing created threads.
    Improve the response speed, when the task arrives, the task can be executed immediately without waiting for the thread to be created.
    Improve the manageability of threads. Threads are scarce resources. If created without limit, it will not only consume system resources, but also reduce the stability of the system. Using the thread pool can be used for unified allocation, tuning and monitoring.

2.1 Working principle of thread pool

insert image description here

2.2 Detailed Explanation of ThreadPoolExecutor

  • How to use thread pool?
    To use the thread pool, let's first understand the ThreadPoolExecutor class.
    This class has four construction methods, and the other three will call one of them. Let's see what parameters this construction method contains
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

1. corePoolSize: the number of core threads, the number of core threads that can be created by the thread pool to perform tasks
2. maximumPoolSize: the maximum number of threads, which means that when the corePoolSize and workQueue are full, the thread pool will create non-core threads to perform tasks, and the maximumPoolSize is the thread The pool can survive the maximum number of threads.
3. keepAliveTime: The maximum waiting time, which refers to the waiting time of the number of non-core threads. After the core thread is created, it will permanently exist and wait for the task to arrive. The non-core thread will be destroyed after executing the task. When the idle time exceeds keepAliveTime, the non-core thread will be destroyed. Core thread
4, unit: time unit of keepAliveTime
//TimeUnit.DAYS; days
//TimeUnit.HOURS; hours
//TimeUnit.MINUTES; minutes
//TimeUnit.SECONDS; seconds
//TimeUnit.MILLISECONDS; milliseconds
//TimeUnit.MICROSECONDS ; Subtle
//TimeUnit.NANOSECONDS; Nanosecond
5, workQueue: Waiting queue, when the number of threads in the thread pool reaches corePoolSize, tasks will be placed in the waiting queue
Common waiting queues have the following three types
1) SynchronousQueue direct submission strategy
2) LinkedBlockingQueue unbounded queue
3) ArrayBlockingQueue:, a bounded blocking queue based on an array structure, this queue sorts elements according to the FIFO (first in first out) principle
When the number of core threads in this queue reaches the maximum value, newly submitted tasks will be stored in the queue. When the queue is full, non-core threads will be created. When the total number of threads reaches the maximumPoolSize, a rejection strategy will be used to process newly submitted tasks.

  1. SynchronousQueue direct submission strategy

  2. LinkedBlockingQueue Unbounded queue
    The waiting queue is an unbounded queue, that is, it will be added without limit, non-core threads will never be created, and newly added tasks will always wait for core threads to execute.

  3. ArrayBlockingQueue is a bounded queue.
    When the number of core threads reaches the maximum value, newly submitted tasks will be stored in the queue. When the queue is full, non-core threads will be created. When the total number of threads reaches the maximumPoolSize, a rejection strategy will be used to process newly submitted tasks.

  4. PriorityBlockingQueue an unbounded queue with priority

6. threadFactory: Custom thread factory
7. Handler: Rejection strategy, the rejection strategy called when the number of threads in the thread pool exceeds the maximumPoolSize, the common types are as follows
1) AbortPolicy: Abort strategy. The default rejection policy directly throws RejectedExecutionException
2) DiscardPolicy: directly discards the requested task
3) DiscardOldestPolicy: discards the first task in the queue, and then adds this task to the queue
4) CallerRunsPolicy: The caller thread executes the task . That is, occupying the threads that send tasks to the thread pool and occupying the main thread can relieve the internal pressure of the thread pool.

  • Executors provide several common thread pool creation methods
    1) newFixedThreadPool: A thread pool with a fixed number of threads. The number of core threads is equal to the maximum number of threads, and the work queue uses an unbounded LinkedBlockingQueue. It is suitable for scenarios where the number of current threads needs to be limited in order to meet the requirements of resource management, and it is suitable for servers with heavy loads.
    2) newSingleThreadExecutor: A thread pool with only one thread, that is, a single-threaded thread pool. Applicable to scenarios that require the execution of tasks in order.
    3) newFixedThreadPool: Fixed thread thread pool, corePoolSize = maximumPoolSize, and LinkedBlockingQueue is selected for the blocking queue, which is suitable for scenarios where the number of current threads is limited.
    4) newCachedThreadPool: The number of core threads in this thread pool is 0, the maximum number of threads is Integer.MAX_VALUE, and the thread survival time is 60s. This thread pool can be expanded infinitely and idle threads will be destroyed in a short time. It is suitable for servers with light loads.

Guess you like

Origin blog.csdn.net/pgcdnameming/article/details/121784953