Java线程池的学习

Java线程池的学习

1. 线程池是什么(实现原理)?

线程池指的是管理一组同构工作线程的资源池。
实现原理其实也很简单,一个线程集合workSet和一个阻塞队列BlockingQueue,每次取一个工作线程,执行队列中的任务,执行完后再放回线程集合中。

1.1 线程池的好处?

1.重用现有的线程,相比于为每个任务创建一个线程,减少了线程创建与销毁的开销。
2.提高任务的响应性,大部分情况下,任务到达时工作线程已经存在,可以直接执行任务,而不必等待线程创建。
3.提高CPU的利用率,适当地调整线程池的大小,使CPU处于忙碌状态,也可减少过多的线程产生的竞态问题。

1.2 线程池的种类?

Executors类中提供了多种创建线程池的静态工厂方法
image.png
其中:
1.CachedThreadPool是可缓存的线程池,线程数随着任务处理需求的增长而增长,在任务处理需求降低时,也会回收一定的线程。
2.FixedThreadPool是固定长度的线程池,提交一个任务就创建一个线程执行它,直到达到线程池的最大容量后,线程数不会再发生变化。(如果有线程异常结束,则创建一个线程递补)
3.ScheduledThreadPool是固定长度的线程池,任务以延迟或者定时的方式执行,
4.SingleThreadPool是单线程的,工作队列中的任务将串行执行,可用作FIFO、LIFO、优先级等。
5.WorkStealingPool是工作窃取线程池(jdk8新增),使用ForkJoinPool实现,为每个线程维护了多个工作队列,如果有线程空闲,可以去忙碌线程的工作队列中偷取任务执行,最大地提高CPU的利用率。

1.3 线程池的状态?

1.线程有几种状态?转化关系?
pic_threadpool02.png
2.线程池有几种状态?(Executor的生命周期)
pic_threadpool03.png

2.线程池的使用?

线程池的使用一般通过JUC中的ThreadPoolExecutor类实现,或者是Spring中ThreadPoolTaskExecutor实现(Spring对ThreadPoolExecutor类进行了增强与封装,内部还是通过ThreadPoolExecutor类具体实现线程池)。
线程池中核心参数:
image.png
线程池的简单使用:
image.png

2.1为什么线程池必须设置合适的执行策略?

特殊的任务需要特殊的执行策略
1.依赖性任务: 一个任务依赖于另一个任务执行,可能会产生死锁(线程饥饿死锁)。
2.使用线程封闭的任务: 线程封闭任务需要串行执行,所以线程池需要是单线程的。
3.对响应时间敏感的任务: 运行时间长的任务提交到一个线程数不多的线程池中,会造成线程拥塞,影响其他执行时间短的任务的响应时间,可以通过限制任务的等待时间来解决,例如:
Thread.join(long timeout)
BlockingQueue.offer/poll(E e, long timeout, TimeUnit unit)
CountDownLatch.await(long timeout, TimeUnit unit)
Selector.select(long timeout)
如果等待超时,则将任务标记为失败,此时可以中止任务或者将任务重新插入队列以便继续执行。
4.使用了ThreadLocal的任务: 线程封闭需要串行执行,所以线程池需要是单线程的。

2.2设置线程池的大小的考虑因素?

两个参数:
corePoolSize基本大小: 即没有任务执行时,线程池的大小,只有在工作队列满了的情况下,才会创建超出这个数量的线程。
maximumPoolSize最大大小: 即可同时活动的线程数量的上限,如果某个线程的空闲时间超过了存活时间,则这个线程会被标记为可回收的,此时线程数量如果大于基本线程数corePoolSize,则此线程会被中止。
线程池大小的设置需要考虑实际的运用情景,需要分析计算环境、资源预算与任务的特性,要考虑任务的是计算密集型还是I/O密集型,是否需要稀缺资源(如JDBC连接),任务的类型是否相似,如果差别很大,那么不同的任务需要不同的线程池。
1.计算密集型任务: 对于此类任务线程池大小设置为 CPU核心数 + 1,线程偶尔会遇到突发问题而阻塞暂停(如页缺失故障)所以增加一个线程以保证这种情况下也不会有CPU闲置,已达到利用率的最大化。
2.I/O密集型任务: 对于I/O密集型任务,由于I/O操作一般比较耗时,线程需要阻塞等待,那么就需要更多的线程以保证一部分线程阻塞的时候,有另外的线程在执行任务,不会产生CPU闲置的情况。一般设置为 2 * CPU核心数。

如果任务需要某些稀缺资源,比如说需要连接资源池,比如数据库的连接池, 如果每个任务都需要连接数据库,那么数据库连接池的大小会限制线程池的大小,相反如果此线程池是数据库连接的唯一使用者,那么线程池的大小又会限制数据库连接池的大小。

2.3不同的任务队列BlockingQueue?

BlockingQueue可以有三种:无界队列、有界队列和同步移交(Synchronous Handoff)
1.无界的LinkedBlockingQueue: FixedThreadPool和SingleThreadPool的默认工作队列,队列无限增加。
2.有界的LinkedBlockingQueue和ArrayBlockingQueue: 这类工作队列有限定大小,队列满了之后还有任务到来,会有相应的拒绝策略来处理,此类队列是先进先出的。
3.PriorityBlockingQueue: 是优先级队列,可自定义比较器。有初始容量(默认为11)可以指定容量,然后队列装满之后,有新的元素需要入队,则会动态扩容。

int newCap = oldCap + ((oldCap < 64) ?
                       (oldCap + 2) : // grow faster if small
                       (oldCap >> 1));

4.SynchronousQueue: CachedThreadPool的默认工作队列,但其并不是一个真正的队列,内部没有容器,而是在线程之间的一种移交机制,内部使用CAS保障线程安全,生产者消费者模型,生产线程生产任务put,如果此时没有消费者接收,则生产者线程会阻塞,有消费者接收任务take,则会唤醒生产线程,并接收执行任务。
5.DelayQueue: ScheduledThreadPool的默认工作队列,DelayQueue底层存储的容器为PriorityQueue,插入其中的任务按设定的delay时间排序(产生冲突则根据插入到队列的先后排序),只有delay时间耗尽的任务可以被取出执行。

2.4任务队列的饱和策略(任务拒绝策略)?

当有界队列被填满之后,需要饱和策略来进行调解。
image.png
1.CallerRunsPolicy: 调用者运行,这种方式既不会抛弃任务,也不会抛出异常,而是将任务退回给调用者,其实就是在调用execute()方法的线程中执行。
2.AbortPolicy: 中止策略,默认的饱和策略,抛出未检查的RejectedExecutionException异常,调用者可以捕获这个异常,自定义处理。
3.DiscardPolicy: 抛弃策略,队列已经满了,抛弃新的任务。
4.DiscardOldestPolicy: 最老抛弃策略,就是抛弃在队列中存在最久的任务。


参考书目:

1.《Java并发编程实战》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
关于 Java 线程池导致 MySQL 的 Bug,可能是由于线程池中的线程数量过多,导致 MySQL 连接池被耗尽,从而出现连接超时或者连接泄露的情况。这种情况下,可以通过优化线程池的配置,增加 MySQL 连接池的大小,或者使用连接池管理工具进行监控和管理,来避免这种情况的发生。 至于关于线程池的问题,可以具体分为以下几个方面: 1. 线程池的大小:线程池的大小需要根据实际的业务场景来进行设置,如果线程池的大小过小,可能会导致任务无法及时处理,而过大则会占用过多的系统资源,影响系统的性能表现。 2. 线程池的类型:线程池的类型包括 FixedThreadPool、CachedThreadPool、ScheduledThreadPool 等,不同类型的线程池适用于不同的场景,需要根据实际的业务需求进行选择。 3. 线程池的拒绝策略:当线程池中的任务数量超过线程池的最大容量时,需要采取一定的拒绝策略,如 AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy 等,需要根据业务场景和系统性能要求进行选择。 4. 线程池的生命周期管理:线程池的生命周期包括创建、启动、运行、停止等多个阶段,在使用线程池时需要对其进行合理的生命周期管理,以确保线程池的稳定运行和性能表现。 总之,线程池是一个非常重要的并发编程工具,需要在实践中不断学习和积累经验,以提高系统的性能和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值