java并发编程--线程池

线程池

为什么使用线程池?

线程生命周期的开销非常高。

  • 1.频繁的创建,销毁线程需要使用额外的时间,资源。

资源消耗

  • 1.若线程过多,会对系统造成巨大负担。
  • 2.空闲的线程过多,会占用大量的内存,给垃圾回收器带来压力。
  • 3.大量的线程竞争CPU会导致过多的CPU上下文切换的开销和性能开销,创建过多的线程会导致性能降低。

稳定性

  • 1.在可创建的线程的数量上存在一个限制。过多的线程可能会导致OutOfMemoryError(每个线程维护2个执行栈,一个用于java代码,另一个用于原生代码。通常,JVM在默认情况下会生成一个复合的栈,大小约为0.5MB(可以通过JVM标志-Xss或者通过Thread 的构造函数来修改这个值)如果将2^32除以每个线程的栈大小,那么线程将被限制为几千到几万)

创建线程池的方式

通过Executors的静态工厂方法创建

  • newFixedThreadPool:创建一个固定的大小的线程池,每提交一个任务时就创建一个线程,直到达到线程的最大数量

  • newCacheThreadPool:创建可缓存的线程,可回收空闲的线程,或创建线程。线程池的数量规模不受限制。

  • newSingleThreadPool:一个单线程的线程池。若线程异常结束,则使用另一个线程替代。

  • newScheduledThreadPool:创建固定长度的线程池。以延时或定时的方式来执行任务。

线程池的生命周期

  • Executor 的实现通常会创建线程来执行任务。但JVM只有在所有(非守护)线程全部终止后才会退出。

线程池的接口

  • Executor :只含有任务提交的方法: execute(Runnable task);
  • ExecutorService:为了解决执行服务的生命周期问题,ExecutorService添加了一些用于生命周期管理的方法。

以上内容摘自 《java并发编程实战》

为什么不同的业务使用不同的线程池?(以下为个人总结,不能保证正确性)

  • 1.一些耗时的长业务和一些响应快的短业务在一起执行,会阻塞影响短业务,业务之间相互影响。
  • 2.IO密集 和 CPU密集 的服务应该采用不同的线程池。

相关资料

有时候我们会发现新上线的部分业务出现了问题,并且影响了其他功能。
我有时候想到从架构上如何避免这个问题,然后有了这样的一个思路。

开发的时候我们基本上不会考虑到这种问题,整个服务就共用一个线程池,甚至有些系统是单线程的。
一旦出现问题整个服务就一起挂掉了
这个肯定是我们不想看到的。
解决这个问题方法就是把不同模块放在不同的线程里面,如果之前使用的是线程池那么 
不同业务也要用不同的线程池分开。因为如果这个业务有问题,这个业务所在的线程池也会很快的阻塞掉。
如果不同的业务分开到不同的线程池里面去,至少不会因为这个业务导致其他业务不可用。

再配合上一篇干掉耗时任务的方法,可以保证线上服务不会全完蛋

构建更健壮的系统:不同的业务放在不同的线程/线程池里面

合理的配置线程池

要想合理的配置线程池,就必须首先分析任务特性,可以从以下几个角度来进行分析:

任务的性质:CPU密集型任务,IO密集型任务和混合型任务。
任务的优先级:高,中和低。
任务的执行时间:长,中和短。
任务的依赖性:是否依赖其他系统资源,如数据库连接。
任务性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务配置尽可能小的线程,
如配置Ncpu+1个线程的线程池。IO密集型任务则由于线程并不是一直在执行任务,则配置尽可能多
的线程,如2*Ncpu。混合型的任务,如果可以拆分,则将其拆分成一个CPU密集型任务和一个IO密集型
任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐率要高于串行执行的吞吐率,
如果这两个任务执行时间相差太大,则没必要进行分解。我们可以通过
Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。

聊聊并发(三)——JAVA线程池的分析和使用

猜你喜欢

转载自blog.csdn.net/qq_24489717/article/details/78486634