一段JAVA线程池的设置引发的血案
来源:互联网 发布:淘宝店铺认证在哪里 编辑:程序博客网 时间:2024/05/21 09:44
应公司需求我们对一个项目进行了线上压力测试,结果发现,三台服务器一共只有59TPS,结果惨不忍睹。
那么针对这样的场景,我们利用一周时间进行专注性的优化,寻找性能的瓶颈点。
第一步:我们针对线上的环境进行模拟,尽量真实的在测试环境中再现,采用数据库连接池为咱们默认的C3P0。
那么当压测到二万批,100个用户同时访问的时候,并发量突然降为零!报错如下:
Could not get JDBC Connection; nested exception is java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
针对以上错误跟踪C3P0源码,以及在网上搜索资料(http://blog.sina.com.cn/s/blog_53923f940100g6as.html)发现,C3P0在大并发下表现的性能不佳。
第二步:针对这个问题进行数据库连接池优化,更换了BoneCPDataSource,以及Apache BasicDataSource后,发现报错如下:
java.lang.OutOfMemoryError: unable to create new native thread, dubbo version: 2.5.4, current host: 192.168.122.1java.lang.OutOfMemoryError: unable to create new native thread
第三步:由此可以判断,问题不在于连接池的问题,于是在压测的时候,将DUMP日志导出进行分析发现,项目中启动了一万多个线程,而且每个线程都极为忙碌,彻底将资源耗尽。
于是迅速定位到代码,发现如下代码:
private static final ExecutorService executorService = Executors.newCachedThreadPool();/*** 异步执行短频快的任务* @param task*/public static void asynShortTask(Runnable task){executorService.submit(task);//task.run();} CommonUtils.asynShortTask(new Runnable() { @Override public void run() { String sms = sr.getSmsContent(); sms = sms.replaceAll(finalCode, AES.encryptToBase64(finalCode, ConstantUtils.getDB_AES_KEY())); sr.setSmsContent(sms); smsManageService.addSmsRecord(sr); } });
那么问题到底在哪里呢???就在这一行!
private static final ExecutorService executorService = Executors.newCachedThreadPool();
在并发的情况下,无限制的申请线程资源造成性能严重下降,在图表中显抛物线形状的元凶就是它!!!那么采用这种方式最大可以产生多少个线程呢??答案是:Integer的最大值!看如下源码:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory);}
那么尝试修改成如下代码:
private static final ExecutorService executorService = Executors.newFixedThreadPool(50);
修改完成以后,并发量重新上升到100以上TPS,但是当并发量非常大的时候,项目GC(垃圾回收能力下降),分析原因还是因为 Executors.newFixedThreadPool(50)这一行,虽然解决了产生无限线程的问题,但是
当并发量非常大的时候,采用newFixedThreadPool这种方式,会造成大量对象堆积到队列中无法及时消费,看源码如下:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
可以看到采用的是无界队列,也就是说队列是可以无限的存放可执行的线程,造成大量对象无法释放和回收。
结论:
目前我们的项目还在持续优化中,还没有最终优化完成,目标是要把项目优化完善,但此次事件,再次提醒我们,在使用线程池的时候,一定要把握其细节,深入了解其原理再使用,不要随意使用,任何线程池的使用方式都有不同的使用场景,并不是只要使用了线程池就万事大吉,还有很多工作需要我们去注意。
- 一段JAVA线程池的设置引发的血案
- 线程池学习-【转】一段JAVA线程池的设置引发的血案
- 一段代码引发的血案
- 一段代码引发血案(查看你的杀毒软件灵敏度)
- GC与Memory pool引发的一段血案
- Druid连接池一个设置引发的血案
- Eclipse断点设置引发的血案...
- Java Memory Model引发的血案
- JAVA由遍历Map引发的血案
- ActiveX引发的“血案”
- size_t引发的血案
- 一个 * 引发的血案
- gets引发的血案
- Print 引发的“血案”
- lease引发的血案
- 一个“-”引发的血案
- MD5引发的血案
- 一个"/"引发的血案
- 【转】Java常见内存溢出异常分析
- hdu1698 Just a Hook (线段树,区间更新)
- 将一张表中数据插入到另外一张中的脚本
- GC日志分析
- 【转】Scala从零开始:使用Intellij IDEA写hello world
- 一段JAVA线程池的设置引发的血案
- android检查sd卡是否可写
- 【机器学习算法系列之一】EM算法实例分析
- hdu 2255
- 【转】如何用消息系统避免分布式事务?
- Using Redis as an LRU cache文章翻译
- Java设计模式——代理模式
- 【转】使用 VisualVM 进行性能分析及调优
- Rails Respond Format 应用