ThreadPoolExecutor运行原理

本文详细介绍了Java线程池的概念及其实现方式,包括使用Executors框架创建固定大小的线程池,以及如何利用ThreadPoolExecutor进行更精细的线程池管理。通过具体的代码示例,展示了线程池的工作原理及其在任务调度中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程池用来管理工作线程,它包含一个队列用来存放等待执行的任务。java的java.util.concurrent.Executors提供了实现java.util.concurrent.Executor这个接口的方法,用来创建线程池。

下面从一个简单的例子解释它的运行机制。
创建Runnable类

public class WorkerThread implements Runnable {
    private String command;
    public WorkerThread(String s){
        this.command=s;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+' Start. Command = '+command);
        processCommand();
        System.out.println(Thread.currentThread().getName()+' End.');
    }
    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    public String toString(){
        return this.command;
    }
}

给出测试程序,使用Executors框架创建固定大小的线程池。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleThreadPool {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread('' + i);
            executor.execute(worker);
          }
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println('Finished all threads');
    }
}

上面的程序中,创建了固定大小的线程池,能容纳5个工作线程,然后向这个线程池中提交10个任务,因为线程池的大小为5,先执行其中的5个任务,剩余的5个任务处于等待状态,只要其中的一个任务完成结束,处于等待队列的任务的工作线程取出并执行。上面程序的运行结果如下:

pool-1-thread-2 Start. Command = 1
pool-1-thread-4 Start. Command = 3
pool-1-thread-1 Start. Command = 0
pool-1-thread-3 Start. Command = 2
pool-1-thread-5 Start. Command = 4
pool-1-thread-4 End.
pool-1-thread-5 End.
pool-1-thread-1 End.
pool-1-thread-3 End.
pool-1-thread-3 Start. Command = 8
pool-1-thread-2 End.
pool-1-thread-2 Start. Command = 9
pool-1-thread-1 Start. Command = 7
pool-1-thread-5 Start. Command = 6
pool-1-thread-4 Start. Command = 5
pool-1-thread-2 End.
pool-1-thread-4 End.
pool-1-thread-3 End.
pool-1-thread-5 End.
pool-1-thread-1 End.
Finished all threads

Executors类使用ThreadPoolExecutor
提供了ExecutorService的简单实现1,但是ThreadPoolExecutor提供了更多的特征。在代码中,当创建ThreadPoolExecutor实例时,可以控制存活线程的数量、限制线程池的大小和实现RejectedExecutionHandler处理不能在等待队列中容纳的任务2

下面先实现RejectedExecutionHandler接口的类。

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 
    {
        System.out.println(r.toString() + ' is rejected');
    }
}

下面创建监控线程用来打印不同时间段executor的执行情况。

import java.util.concurrent.ThreadPoolExecutor;
public class MyMonitorThread implements Runnable
{
    private ThreadPoolExecutor executor;
    private int seconds;
    private boolean run=true;
    public MyMonitorThread(ThreadPoolExecutor executor, int delay)
    {
        this.executor = executor;
        this.seconds=delay;
    }
    public void shutdown(){
        this.run=false;
    }
    @Override
    public void run()
    {
        while(run){
                System.out.println(
                    String.format('[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s',
                        this.executor.getPoolSize(),
                        this.executor.getCorePoolSize(),
                        this.executor.getActiveCount(),
                        this.executor.getCompletedTaskCount(),
                        this.executor.getTaskCount(),
                        this.executor.isShutdown(),
                        this.executor.isTerminated()));
                try {
                    Thread.sleep(seconds*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }
}

下面使用ThreadPoolExecutor创建线程池。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class WorkerPool {
    public static void main(String args[]) throws InterruptedException{
        //RejectedExecutionHandler implementation
        RejectedExecutionHandlerImpl rejectionHandler = new RejectedExecutionHandlerImpl();
        //Get the ThreadFactory implementation to use
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //creating the ThreadPoolExecutor
        ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), threadFactory, rejectionHandler);
        //start the monitoring thread
        MyMonitorThread monitor = new MyMonitorThread(executorPool, 3);
        Thread monitorThread = new Thread(monitor);
        monitorThread.start();
        //submit work to the thread pool
        for(int i=0; i<10; i++){
            executorPool.execute(new WorkerThread('cmd'+i));
        }
        Thread.sleep(30000);
        //shut down the pool
        executorPool.shutdown();
        //shut down the monitor thread
        Thread.sleep(5000);
        monitor.shutdown();
    }
}

在初始化ThreadPoolExecutor时,设置线程池的大小corePoolSize为2,这时没有工作线程(work thread)。随着ThreadPoolExecutor 对任务的提交(submit),新的工作线程被创建,这些线程不会被空闲(idle),直到线程的数量达到corePoolSize = 2。接下来新来的任务会被放在工作队列(work queue)中,直到工作队列满了(等待队列大小为2),这时新的工作线程又被创建,线程的数量直到达到maximumPoolSize =4,超出的任务有RejectedExecutionHandlerImpl处理。


corePoolSize

在创建ThreadPoolExecutor时,应该注意上述参数的设置 3

运行结果:

pool-1-thread-1 Start. Command = cmd0
pool-1-thread-4 Start. Command = cmd5
cmd6 is rejected
pool-1-thread-3 Start. Command = cmd4
pool-1-thread-2 Start. Command = cmd1
cmd7 is rejected
cmd8 is rejected
cmd9 is rejected
[monitor] [0/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, isTerminated: false
[monitor] [4/2] Active: 4, Completed: 0, Task: 6, isShutdown: false, >isTerminated: false
pool-1-thread-4 End.
pool-1-thread-1 End.
pool-1-thread-2 End.
pool-1-thread-3 End.
pool-1-thread-1 Start. Command = cmd3
pool-1-thread-4 Start. Command = cmd2
[monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
[monitor] [4/2] Active: 2, Completed: 4, Task: 6, isShutdown: false, isTerminated: false
pool-1-thread-1 End.
pool-1-thread-4 End.
[monitor] [4/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, >isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [2/2] Active: 0, Completed: 6, Task: 6, isShutdown: false, isTerminated: false
[monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true
[monitor] [0/2] Active: 0, Completed: 6, Task: 6, isShutdown: true, isTerminated: true

ThreadPoolExecutor 构造方法

 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long
 keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue,
 ThreadFactory threadFactory, RejectedExecutionHandler handler)
参数含义
corePoolSize线程池的基本大小,如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务
maximumPoolSize线程池中允许的最大线程数,当线程数量大于maximumPoolSize,超出的线程由丢弃机制来处理
workQueue等待队列,当线程的数量达到corePoolSize时,就向该等待队列放入线程信息(默认为一个LinkedBlockingQueue
keepAliveTime当线程池中的线程数量大于corePoolSize的时候,多余的线程会等待keepAliveTime长的时间,如果无请求可处理就自行销毁
threadFactory4用来构造Thread对象,主要包括线程名称,守护线程的状态,线程的优先级以及设置UncaughtExceptionHandler
handler超过maximumPoolSize之后丢弃处理的方法,java提供了5种丢弃处理的方法,也可以自定义,实现RejectedExecutionHandler接口

参考资料:
Java Thread Pool Example using Executors and ThreadPoolExecutor


  1. A. Executors.newCachedThreadPool() 无限大小的线程池,线程会自动重用。相当于new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())SynchronousQueue 可以存储元素,但是总是报“I am full”,导致 ThreadPoolExecutor 创建新的线程去SynchronousQueue 中拉取任务并运行该任务。线程数量是无界的,队列是有界的,用它处理大批量任务时比较危险的。
    B. Executors.newFixedThreadPool(int) 固定线程数的线程池。相当于new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) ,线程数量是有界的,而工作队列是无界的,如果负载超过了当前的处理能力,那么工作队列会堆积越来越多的任务,有可能会导致内存不足的问题。
    C. Executors.newSingleThreadExecutor() 单线程执行器。
  2. 如果线程池中线程数目已达到maximumPoolSize,则提交的任务交由RejectedExecutionHandler处理丢弃。对于任务丢弃,ThreadPoolExecutor以内部类的形式实现了4个策略:
    - CallerRunsPolicy:提交任务的线程自己负责执行这个任务;
    - AbortPolicy:使Executor抛出异常,通过异常做处理;
    - DiscardPolicy:丢弃提交的任务;
    - DiscardOldestPolicy:丢弃掉队列中最早加入的任务。
    在调用构造方法时,参数中未指定RejectedExecutionHandler情况下,默认采用AbortPolicy
  3. 创建ThreadPoolExecutor 应该避免设置corePoolSize过小,maximumPoolSize过大以及过大的无界队列LinkedBlockingQueue,容易造成工作队列未满,而抛出OutOfMemoryError 异常。
  4. 给线程设置一个好的有状态的名字,如Thread.currentThread().setName(Context + TID + Params + current Time,..) ,具体请参考这里。当线程中运行一个任务时发生未捕获异常,默认情况下线程终止,异常信息打印在终端,这种默认的行为并不恰当。在创建线程时可以定制未捕获异常的处理。如:new UncaughtExceptionHandler() {
    @Override public void uncaughtException(Thread t, Throwable e) {
    System.err.println(String.format( "Thread %s threw exception - %s", t.getName(), e.getMessage())) } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值