Java并发线程池的介绍和使用

一、线程池基本介绍

1、线程池

线程池:一个管理线程的池子。

2、为什么使用线程池?

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 提高响应速度。当任务到达时,可以不需要等到线程创建就能立即执行。 提高线程的可管理性。统一管理线程,避免系统创建大量同类线程而导致消耗完内存。

3、线程池执行原理

  1. 当线程池里存活的线程数小于核心线程数 corePoolSize 时,这时对于一个新提交的任务,线程池 会创建一个线程去处理任务。当线程池里面存活的线程数小于等于核心线程数 corePoolSize 时, 线程池里面的线程会一直存活着,就算空闲时间超过了 keepAliveTime ,线程也不会被销毁,而 是一直阻塞在那里一直等待任务队列的任务来执行。
  2. 当线程池里面存活的线程数已经等于corePoolSize了,这是对于一个新提交的任务,会被放进任务 队列workQueue排队等待执行。
  3. 当线程池里面存活的线程数已经等于 corePoolSize 了,并且任务队列也满了,假设 maximumPoolSize>corePoolSize ,这时如果再来新的任务,线程池就会继续创建新的线程来处 理新的任务,知道线程数达到 maximumPoolSize ,就不会再创建了。 4. 如果当前的线程数达到了 maximumPoolSize ,并且任务队列也满了,如果还有新的任务过来,那 就直接采用拒绝策略进行处理。默认的拒绝策略是抛出一个RejectedExecutionException异常。

二、线程池的使用

1.基本线程池种类

常见的线程池有 FixedThreadPool 、 SingleThreadExecutor 、 CachedThreadPool 和 ScheduledThreadPool 。这几个都是 ExecutorService 线程池实例。

  1. FixedThreadPool 固定线程数的线程池。任何时间点,最多只有 nThreads 个线程处于活动状态执行任务,使用无界队列 LinkedBlockingQueue(队列容量为 Integer.MAX_VALUE)在任务比较多的时 候会导致 OOM。
  2. SingleThreadExecutor 只有一个线程的线程池,使用无界队列 LinkedBlockingQueue(队列容量为 Integer.MAX_VALUE)在任务比较多的时 候会导致 OOM。
  3. CachedThreadPool 根据需要创建新线程的线程池。CachedThreadPool 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。
  4. ScheduledThreadPoolExecutor 在给定的延迟后运行任务,或者定期执行任务。在实际项目中基本不会被用到,因为有其他方案选择比 如 quartz 。

2、线程池大小怎么设置?

如果线程池线程数量太小,当有大量请求需要处理,系统响应比较慢,会影响用户体验,甚至会出现任 务队列大量堆积任务导致OOM。 如果线程池线程数量过大,大量线程可能会同时抢占 CPU 资源,这样会导致大量的上下文切换,从而增 加线程的执行时间,影响了执行效率。

  1. CPU 密集型任务(N+1): 这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1 , 多出来的一个线程是为了防止某些原因导致的线程阻塞(如IO操作,线程sleep,等待锁)而带来的影 响。一旦某个线程被阻塞,释放了CPU资源,而在这种情况下多出来的一个线程就可以充分利用 CPU 的 空闲时间。

  2. I/O 密集型任务(2N): 系统的大部分时间都在处理 IO 操作,此时线程可能会被阻塞,释放CPU资源, 这时就可以将 CPU 交出给其它线程使用。因此在 IO 密集型任务的应用中,可以多配置一些线程,具体 的计算方法: 最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU核心数 * (1 + (IO耗时/CPU耗时)) , 一般可设置为2N。

    这些是理论上的配置,实际开发中有条件的话,应根据压测情况配置实际参数值。

3、实际使用的线程池ThreadPoolExecutor

ThreadPoolExecutor 的通用构造函数:

  1. corePoolSize :当有新任务时,如果线程池中线程数没有达到线程池的基本大小,则会创建新的线 程执行任务,否则将任务放入阻塞队列。当线程池中存活的线程数总是大于 corePoolSize 时,应该考虑 调大 corePoolSize。

  2. maximumPoolSize :当阻塞队列填满时,如果线程池中线程数没有超过最大线程数,则会创建新的 线程运行任务。否则根据拒绝策略处理新任务。非核心线程类似于临时借来的资源,这些线程在空闲时 间超过 keepAliveTime 之后,就应该退出,避免资源浪费。

  3. BlockingQueue :存储等待运行的任务。

  4. keepAliveTime :非核心线程空闲后,保持存活的时间,此参数只对非核心线程有效。设置为0, 表示多余的空闲线程会被立即终止。

  5. TimeUnit :时间单位

  6. 拒绝策略RejectedExecutionHandler

    AbortPolicy:默认的策略,直接抛出RejectedExecutionException
    DiscardPolicy:不处理,直接丢弃
    DiscardOldestPolicy:将等待队列队首的任务丢弃,并执行当前任务
    CallerRunsPolicy:由调用线程处理该任务

面试题

1、进程线程的概念和区别?

  • 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间。

  • 线程是比进程更小的执行单位,它是在一个进程中独立的控制流,一个进程可以启动多个线程,每条线 程并行执行不同的任务。

2、创建线程有哪几种方式?

  1. 通过扩展 Thread 类来创建多线程
  2. 通过实现 Runnable 接口来创建多线程
  3. 实现 Callable 接口,通过 FutureTask 接口创建线程。
  4. 使用 Executor 框架来创建线程池
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值