`

慎用Java 自带两种线程池!

阅读更多

 

      Java5相比之前的Java版本,在并发编程上,有了非常大的提高,加了很多类,提供了很多可用于并发编程的工具包和工具类。尤其为人们所称道的,就是Java自带的线程池。

 

   Java5线程池的介绍文章,可以说在网上比比皆是,我就不再重复了,只是简单提一下,线程池给并发程序带 来了几个好处:

 

    1、创建和销毁线程的开销

   2、保护系统资源,避免创建太多的线程导致系统崩溃

   3、简化编程模型

 

Java5自带的线程池( ThreadPool),用于并发系统的,主要有:

缓存线程池(newCachedThreadPool):每个任务过来后都会创建一个线程,任务结束后,线程缓存一段时间,下次任务过来后,如果有之前缓存的线程就无须再创建而是直接使用。

 

固定数量线程池(newFixedThreadPool):创建固定线程数量的线程池,如果任务数大于线程池中线程的数量,那么任务将等待。

 

其实,我们看Java的源代码,就会发现,上面两种线程池,都是调用Java的ThreadPoolExecutor来实现的。其构造函数如下:

 

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

 

我们可以看到,实际上是需要制定核心线程数、最大线程数、存活时间和时间单位、选择的队列这些参数。

其中,核心线程数的概念至关重要,当有任务到来时,如果线程池中的线程数还不够核心线程数,则启动一个新的线程来运行任务(即便其它线程在空闲);如果线程池中的线程已经达到核心数,但未达到最大数,则任务放到队列中等待空闲的核心线程处理;除非队列满,不会启动新的线程。

 

从这里,我们再来看缓存线程池和固定数量线程池,非常简单能够看出,缓存线程池的核心数为1,最大数为1,队列长度是1;固定线程池核心数和最大数都是设定的数量。

 

因为Java提供了这两种线程池,所以一般情况下,我们可能都觉得不需要再写别的线程池实现了,直接用这两种吧。结果在产品开发的过程中,笔者就遇到了很大的一个坑,差点掉进去出不来。

 

当时是要开发一款高性能、高稳定性的C/S模式的服务器产品,Java5之后,使用线程池改进了任务派发的部分,当时以为这两种线程池都应该能够满足要求,就先采用固定数量线程池来试,结果发现如果服务器和客户端都采用长连接,固定数量线程池存在很大的设计上的bug。

 

例如任务线程池核心数100,最大数100,客户端也正好100个连接连上了服务器。因为长连接机制下,为了保证处理效率,流程是服务器端ServerSocket在accept之后,不停循环,接受到请求后处理,然后继续等待连接上的数据,直至接收到客户端断开的指令或服务器端超时。

 

如下图:



 

 

可以想象,如果这时候第101个连接连上了服务器,再没有新的连接,TCP连接正常,任务也被接受放到了队列里,那么这个任务就只能乖乖等在队列里等着超时,别的什么都做不了!除非改变上面流程图中的方式,线程不再这样循环,而是直接返回,这个连接上新的数据过来由另外的线程处理,等待线程池重新分配,但这样效率一定不如图中的方式好,否则没有什么好的办法。

 

对于一些使用场景复杂的服务器端,客户端长连接和短连接都可能有的这种场景,使用固定大小线程池,就必须考虑这个问题。或者牺牲效率,或者深入考虑长连线程带来的问题。

 

既然固定大小有问题,那就看看缓存线程池吧,麻烦更大了,如果服务器没有别的辅助控制,一下子涌入大量的客户连接,服务器一下子需要启动大量的线程,很有可能崩溃,这个也是无法接受的。

 

    从这里可以看到,Java自带的两种线程池,其实是各有各自的适用场景,对池中的任务,也都有自己的要求和限制,在适用这些基础设施来设计系统之前,首先应该对这些进行透彻的分析,不要等到出现bug才醒悟。上面提到的bug,因为没有清晰的错误信息,就非常不容易分析出来。

 

    如果你的服务器面临这么复杂多变的客户端,而且既要求效率上不能牺牲,又希望使用Java的线程基础设施,那么一定要在这里慎重再慎重。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 77.7 KB
分享到:
评论
8 楼 windshome 2013-04-27  
crazywen2011 写道
请问,lz的图是用什么画的?

我是用的EDraw,trial版,很不错,其实还可以用smartdraw,visio也行。
7 楼 crazywen2011 2013-04-27  
请问,lz的图是用什么画的?
6 楼 windshome 2013-03-21  
其实新的感悟很多,只是没有太多的时间去整理归类。就把之前的东西草草收拾一下发上来了,总比埋没在尘埃里要强啊,有足够的时间,一定好好整理一下,加上一些最新的内容,归根到底,还是希望对看自己博文的人,能有一些价值,能有所收获。
5 楼 hunter8v 2013-03-21  
以前工作的总结啊,哈哈!
4 楼 windshome 2013-03-19  
huangyunbin 写道
其实你的第一种方案是需要第二中方案补充的,不然你一直启动新的线程,线程太多会撑死服务器的。

而且这个不能根本上解决问题,你的连接是长连接,所以一个线程分配出去,要等到这个长连接结束才能归还到线程池。但是长连接的时间比较长,当同时大量请求,这个时候会超过你设置的线程最大数,那么这个时候其实后面的用户是连接不上的。

我其实感觉这个不是线程池的坑,而是你的长连接和大量用户同时请求的坑,是你自己挖的坑。


长连接和大量用户同时请求的坑,我觉得只能加服务器了,要不就是改变这种方式。


说的很对,是我回复的时候没有注意,如果不限制线程的增长,会撑死服务器的。

从产品需求上说,是因为不肯放弃在一个线程中处理长链接带来的效率提高,而且还想让服务器能够同时应对随时可能到来的新连接(即便新的连接不多,但是也不能连接无限等待啊)这样一个需求,导致使用的时候出问题。

或者干脆,长链接上处理多个业务时,改为再次进入线程池,也能解决问题;或者在固定大小线程池上变化一下,再衍生出一些根据任务队列长度值调整线程产生策略;或者直接用(2)都可以。

再次对huangyunbin表示感激。
3 楼 huangyunbin 2013-03-19  
其实你的第一种方案是需要第二中方案补充的,不然你一直启动新的线程,线程太多会撑死服务器的。

而且这个不能根本上解决问题,你的连接是长连接,所以一个线程分配出去,要等到这个长连接结束才能归还到线程池。但是长连接的时间比较长,当同时大量请求,这个时候会超过你设置的线程最大数,那么这个时候其实后面的用户是连接不上的。

我其实感觉这个不是线程池的坑,而是你的长连接和大量用户同时请求的坑,是你自己挖的坑。


长连接和大量用户同时请求的坑,我觉得只能加服务器了,要不就是改变这种方式。
2 楼 windshome 2013-03-18  
感谢回复。其实我解决的办法也无非一是偷懒忍一下,二是自己改。

忍一下是指改变自己使用线程池的方式。如上文所述:

“除非改变上面流程图中的方式,线程不再这样循环,而是直接返回,这个连接上新的数据过来由另外的线程处理,等待线程池重新分配,但这样效率一定不如图中的方式好,否则没有什么好的办法。”


这样能解决固定大小线程池及类似线程池的问题。

自己改有几种方案:

(1)在固定大小线程池基础上改,改变新线程产生机制,设定一个任务等待的最大时间,超过时间得不到处理,启动新的线程进行处理,而不是“死等”

(2)在缓存线程池基础上改,加入资源保护机制,限制线程的数目,保护服务器本身

大家有好的方法也说一说啊,我是从自己产品开发实践中找了这样的方式,因为时间紧没有考虑太多。


1 楼 huangyunbin 2013-03-18  
我想问下楼主最后是怎么解决这个问题的。

相关推荐

    [] - 2023-04-18 Java 陷阱:慎用入参做返回值!!!.pdf

    互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术,人工智能互联网资讯,技术简介,IT、AI技术...

    加密的惨剧!慎用文件夹加密软件!

    加密的惨剧!慎用文件夹加密软件! 加密的惨剧!慎用文件夹加密软件!

    java中慎用继承 以及java中的stack的败笔

    因此,当面临代码复用需求时,应优先考虑聚合和组合这两种设计模式。 聚合和组合是比继承更灵活的代码复用方式。在聚合关系中,一个对象(容器)包含另一个对象(组件),但两者之间不存在所有权关系,如`...

    请慎用java的File#renameTo(File)方法

    java的file rename方法 以前我一直以为File#renameTo(File)方法与OS下面的 move/mv 命令是相同的,可以达到改名、移动文件的目的。不过后来经常发现问题:File#renameTo(File)方法会返回失败(false),文件没有移动,...

    暴力删除一切顽固文件,慎用!

    把要删除的文件拖到这个文件上面即可。注意,这是强力删除。一定慎用,因为连垃圾箱都会没有的。

    java大作业-聊天室,有待完善,慎用

    JavaFX是Java平台上的一个强大的用户界面工具包,用于创建...但是,由于标题中提到“有待完善,慎用”,说明这个项目可能还在开发阶段,可能存在一些未解决的问题,因此在使用时需要谨慎,可能需要进一步完善和调试。

    站长助手(有注入代码,慎用!)

    站长助手相关代码,里面有一点注入代码,慎用!!!

    icop和线程池的lib

    封装的iocp,线程池。效率很高,很实用,不包括发送队列,请自行处理。最大支持1w个sock 解决内存泄露问题 增加accept线程个数,调整返回参数值 ...备注:本次优化针对pt系列汇编指令集,amd慎用 源码不提供

    Isobuster加密光盘破解软件

    遇到加密光盘怎么办?里面的东西拷贝不出来怎么办?使用Isobuster帮你解决这个问题!慎用!!!!!!注意版权。

    易语言处理事件命令慎用时钟版

    易语言处理事件命令慎用时钟版源码,处理事件命令慎用时钟版

    清理maven仓库

    放在仓库目录下,直接双击运行,自动获取目录,自动清理删除本文件夹下所有包含"lastUpdated,*.repositories,*.sha1-in-progress" 的文件。请慎用。!!!!!!

    Java反序列化安全漏洞防控

    2. 慎用反序列化。在不必要的时候避免反序列化操作,或者严格限制和监控反序列化的数据来源。 3. 使用安全的反序列化实践。例如,使用白名单过滤器来限制可以反序列化的类,避免执行未经验证的输入数据。 4. 采用...

    慎用动态编译

    标题“慎用动态编译”指的是在编程过程中对动态编译这一技术的谨慎态度。动态编译是程序运行时根据需要将源代码编译为可执行代码的过程,与静态编译(编译时一次性完成)形成对比。在Java中,JIT(Just-In-Time)...

    阿里巴巴Java开发手册pdf

    推荐使用List接口,慎用Vector,优先选择ArrayList和LinkedList。 三、并发编程 1. 并发控制:正确使用synchronized、volatile,理解并发工具类如Semaphore、CyclicBarrier等。避免并发修改集合,提倡使用并发容器...

    Java Native Interface简介

    - **慎用`GetStringCritical`**:由于它可能导致GC暂停,因此只应在没有内存分配和阻塞操作的短时间段内使用。 在上述示例中,由于`fprintf`可能触发I/O操作,这会阻塞系统,因此在使用`GetStringCritical`后调用它...

    preserve_c_code_attention!!!_在linux下请慎用rm命令_删除了一堆代_c_code.zip

    preserve_c_code_attention!!!_在linux下请慎用rm命令_删除了一堆代_c_code

    金太阳顽固文件夹(不容易删除)

    本工具用批处理所写 然后通过exescript转换了一下 意在交流 运行“金太阳”会在每个盘符创建顽固文件夹。 运行“jintaiyang”会针对性的删除。...慎用!!!!!!!! echo echo echo echo echo echo echo

    JAVA优化编程

    - **慎用反射**:反射操作通常比直接调用方法慢,尽量减少反射的使用。 - **元数据缓存**:使用Class.getDeclaredMethods等方法时,可以缓存结果以提高性能。 8. **监控与分析**: - **使用JVisualVM**:这是一...

    Java复习笔记.rar

    本资源适合已有java基础,但稍微有点忘记的人群,对于初学者可能不够全面,慎下!!!! 本资源中的思维导图为xmind编写,是自己看《Java从入门到精通》时所画的思维导图,由于看完之后感悟不深,后序笔记未整理,只...

    SQL_MSDE清理小程序(慎用)

    SQL和MSDE清理小程序(慎用),SQL和MSDE清理小程序(慎用),SQL和MSDE清理小程序(慎用),

Global site tag (gtag.js) - Google Analytics