Java线程池ThreadPool简单介绍

本文详细介绍了线程池的概念、特点及其实现原理。包括线程池的构造函数、核心参数的作用、任务执行流程、常见的线程池类型如FixedThreadPool、CachedThreadPool等。还探讨了线程池在实际应用中的注意事项。

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

线程池

线程池·好文分享

线程池的特点

  • 避免创建线程和销毁线程的资源消耗,创建线程需要分配内存,列入调度,线程切换时,还需要内存换页,如果CPU的缓存被清空,还需要重新从内存中读取信息
  • 线程池能帮助管理线程
  • 线程池能够提高相应速度,直接从线程池请求线程比创建线程的时间开销小,降低处理请求的延迟
  • 能够避免无限创建线程引起的OutOfMemoryError

线程池的创建

线程池的构造函数

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

七个核心参数的作用:

  • corePoolSize:线程池核心线程数最大值
  • maximumPoolSize:线程池最大线程数,线程总数 = 核心线程数 + 非核心线程数
  • keepAliveTime:线程池中非核心线程的空闲存活时间
  • unit:线程空闲的存活时间单位
  • workQueue:存放任务的阻塞队列
  • threadFactory:设置创建线程的工厂
  • handler:线程池的饱和策略,主要有四种类型

任务执行

执行流程

在这里插入图片描述

  • 提交一个任务,线程池里存活的核心线程数小于线程数corePoolSize时,线程池会创建一个核心线程去处理提交的任务。
  • 如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
  • 当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即最大线程数是否已满,如果没到达,创建一个非核心线程执行提交的任务。
  • 如果当前的线程数达到了maximumPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

BlockingQueue workQueue 线程池中的任务队列

  1. SynchronousQueue:不储存元素的阻塞队列,当接受到任务时,直接交给线程处理,不保留任务,当没有空闲线程时,直接创建新线程处理任务。因为最大线程数可能会限制创建新线程,maximumPoolSize一般指定为Integer.MAX_VALUE
  2. LinkedBlockingQueue:基于链表实现的阻塞队列,当接受任务时,如果当前已经创建的核心线程小于核心线程数,则新建线程(核心线程)处理任务,当创建的核心线程等于核心线程数上限,进入队列等待。这个队列没有最大值限制,因此所有超过核心线程数的任务都被添加至该队列中,maximumPoolSize设定失效
  3. ArrayBlockingQueue:基于数组实现的有界阻塞队列,可以限定队列的长度,接收到任务时,如果当前已创建的线程数小于核心线程数,创建新线程(核心)执行任务,如果达到了,则任务入队等待,如果队列已满,则创建新线程(非核心)执行任务,如果总线程数到达maximumPoolSize,并且队列已满时,发生错误,或执行定义好的饱和策略
  4. DelayQueue:队列内元素(任务)必须实现Delayed接口,接收到任务时,首先先入队,只有到达了指定的延时时间,才会执行任务

拒绝策略

  • AbortPolicy 抛出一个异常,组织线程池的正常工作,默认策略
  • DiscardPolicy 直接丢弃任务,不进行处理
  • DiscardOldestPolicy 丢弃队列中最老的任务,将当前任务继续交给线程池
  • CallerRunsPolicy 交给调用者线程直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务
class MyTask implements Runnable {
	private String id;

	public MyTask(String id) {
		this.id = id;
	}

	public void run() {
		System.out.println(id);
	}
}

public class RejectPolicy {
	public static void main(String[] args) {
		ExecutorService es = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS,
				new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(),
				new ThreadPoolExecutor.CallerRunsPolicy());
		MyTask t1 = new MyTask("id:1");
		MyTask t2 = new MyTask("id:2");
		MyTask t3 = new MyTask("id:3");
		MyTask t4 = new MyTask("id:4");
		MyTask t5 = new MyTask("id:5");
		MyTask t6 = new MyTask("id:6");
		MyTask t7 = new MyTask("id:7");

		es.execute(t1); // pool-1-thread-2: id:1
		es.execute(t2); // pool-1-thread-2: id:2
		es.execute(t3); // pool-1-thread-2: id:3
		es.execute(t4); // pool-1-thread-2: id:4
		es.execute(t5); // pool-1-thread-2: id:5
		es.execute(t6); // main: id:6
		es.execute(t7); // main: id:7
	}
}

Java中常用线程池

FixedThreadPool

可重用固定线程数的线程池,超出的线程会在队列中等待

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

创建线程的方法

public static void main(String[] args) {
        // 参数是要线程池的线程最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);
}

FixedThreadPoolcorePoolSizemaximumPoolSize都设置为nThreads,即只有固定数量的核心线程,不存在非核心线程。keepAliveTime为0L表示多余的线程立即终止,因为没有多余的线程,所以这个参数是无效的。FixedThreadPool采用的任务队列是LinkedBlockingQueue

CachedThreadPool

CachedThreadPool根据需要创建线程的线程池

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

corePoolSize是0,maximumPoolSizeInteger.MAX_VALUE,即该线程池没有核心线程,全部都是非核心线程,且没有上限。keepAliveTime是60秒,空闲线程在60秒后会被自动销毁,此处用到的队列是阻塞队列SynchronousQueue,这个队列没有缓冲区,其中最多只能存在一个任务,有新的任务则阻塞等待

SingleThreadExecutor

SingleThreadExecutor是使用单个线程工作的线程池。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

该线程池总线程数和核心线程数都是1,所以就只有一个核心线程。该线程池采用链表阻塞队列LinkedBlockingQueue,先进先出原则,保证任务按照入队顺序执行

ScheduledThreadPool

ScheduledThreadPool是一个能实现定时和周期性任务的线程池

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

这里创建了ScheduledThreadPoolExecutor,继承自ThreadPoolExecutor,主要用于定时延时或者定期处理任务。

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

corePoolSize是传进来的固定值,maximumPoolSize无限大,因为采用的队列DelayedWorkQueue是无解的,所以maximumPoolSize参数无效。该线程池执行如下:

当执行scheduleAtFixedRate或者scheduleWithFixedDelay方法时,会向DelayedWorkQueue添加一个实现RunnableScheduledFuture接口的ScheduledFutureTask(任务的包装类),并会检查运行的线程是否达到corePoolSize。如果没有则新建线程并启动ScheduledFutureTask,然后去执行任务。如果运行的线程达到了corePoolSize时,则将任务添加到DelayedWorkQueue中。DelayedWorkQueue会将任务进行排序,先要执行的任务会放在队列的前面。在跟此前介绍的线程池不同的是,当执行完任务后,会将ScheduledFutureTask中的time变量改为下次要执行的时间并放回到DelayedWorkQueue中。

线程池参数设置

Java线程池实现原理及其在美团业务中的实践
有赞·记一次故障引发的线程池使用的思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值