如何正确的使用多线程

最近在工作的时候跟一个同事A合作了几个业务点,发现A用了多线程做优化,发现了几点问题,做一下记录

1. A 使用多线程时,在方法中创建了自己的线程池

2. 线程池的大小、队列设置不合理

3. 使用多线程后对业务产生的影响

下面逐一讨论下浅见:

1.  对于一些程序员来说,在业务中随处使用线程池,也就是自己创建自己的线程池,这种方式没有根本上的错误,但这造成了哪些影响呢。

首先我认为对线程池的使用应该是谨慎小心的,对于单个接口、业务来说,多线程处理可以解决接口处理大量数据、复杂业务缓慢的问题,但我们应该在应用中维护开发者自定义的、公共线程池,不应该随用随建,这样涉及到线程池的管理,在业务结束时需要关闭、销毁线程池,这给我们的系统带来的问题是:不断创建了大量的线程池,并且不断去销毁,产生了不必要的内存消耗。

2. A 同学使用了corePoolSize(10)、maximumPoolSize (20)、LinkedBlockingQuene(100)这样的参数,可以看出这是多么的随意,没有考虑到此处业务的数据量,这样的线程池能抗的住并发吗?对现有业务能产生多大的优化呢?

可以看出,当现有业务并发处理到120个时,会产生队列丢弃,而且当数据量持续增大时,系统数据会出错而线上排查困难。

应该怎么做呢,在优化时做数据量、业务量的预期,比如说单应用最大500个客户的1000条数据的处理,corePoolSize(30)、maximumPoolSize (100)这样的初始化参数,业务内部做子线程处理数据时分为10*100,那么线程池的设计应该最少支持500*10-100的任务队列。

3. A在使用多线程时,内部子线程的数量是根据list表数量来分的,造成了什么结果呢?

a. 对应用内部来说,后面的数据库查询会产生 list.size() 次查询

b. 对于下游微服务来说产生了  list.size() 次调用,

可以想象,这是灾难性的使用,用户的一次接口调用,内部竟然产生了如此大量的性能、内存消耗,很有可能将下游微服务接口拖垮,并产生大量的无用的调用日志。

应该怎么做呢?对于 list 表处理,应该对list表进行合理分页,将大数据量的一次处理改成少数据量的多次处理,这样对于应用自己的数据库查询效率提高很多,对下游服务的调用次数也大大减少,方可达到优化的效果并对系统稳健性不产生影响。

 

解决方案:

1. 对于这样的业务处理,并且处理完成后,要做list结果的返回,那么使用了多线程的情况下,还要做最终的汇总返回,我建议使用线程池和 CountDownLatch 处理,CountDownLatch是一个主线程等待子线程的工作模型,初始化方法 CountDownLatch countDownLatch = new CountDownLatch(threadNum), threadNum=10 即为子线程数量,子线程执行完毕后调用 countDownLatch.countDown() 方法做计数器扣减,等待方法countDownLatch.await()等待threadNum = 0 ,所有的子线程处理完毕时会继续执行,做汇总的业务处理、返回。

2. 另外并发包下还有 CyclicBarrier、Semaphore、Exchanger、Phaser等并发工具

 下一篇会写一下 CyclicBarrier的使用,感谢关注,搜索公众号 gosling9527

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值