为什么要使用线程池
在面向对象编程中,对象创建和销毁是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是对一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因。比如大家所熟悉的数据库连接池就是遵循这一思想而产生的,下面将介绍的线程池技术同样符合这一思想。
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:
假设一台服务器完成一项任务的时间为T
T1 创建线程的时间 T2 在线程中执行任务的时间,包括线程间同步所需时间> T3 线程销毁的时间
显然T = T1+T2+T3。注意这是一个极度简化的假设。
可以看出T1,T3是多线程本身附加的开销,用户希望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在应用程序中频繁的创建或销毁线程,这导致T1和T3在T中占有非常大的比例。
线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了,线程池不仅调整T1,T3产生的时间,而且它还显著减少了创建线程的数目。
创建线程&线程池
java创建线程的三种方式:
继承Thread类创建线程类 实现Runnable接口 通过Callable和Future创建线程
java创建线程池的四种方式:
newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程 newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待 newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行 newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定 顺序(FIFO,LIFO,优先级)执行
线程池的好处~
通过重复利用已创建的线程,减少在创建和销毁线程上所花的时间以及系统资源的开销。
提高响应速度,当任务到达时,任务可以不需要等到线程创建就可以立即执行。 提高线程的可管理性,使用线程池可以对线
程进行统一的分配和监控。如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存。
线程池的注意事项:
线程池的大小:多线程应用并非线程越多越好。需要根据系统运行的硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理,线程数与cpu数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小、如果有必要可采用自适应算法来动态调整线程池的大小。以提高cpu的有效利用率和系统的整体性能。
并发错误:多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
线程泄露:这是线程池应用中的一个严重的问题、当任务执行完毕而线程没能返回池中就会发生线程泄露现象。