线程池分析(一)

时间:2019-02-16
本文章向大家介绍线程池分析(一),主要包括线程池分析(一)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

为什么要有线程池?

构建服务器应用程序的简单的模型:当一个请求到达就创建一个新线程,然后在新线程中为请求服务。但实际上服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

于是这样的处理方式极大的耗费资源,影响效率。至于原因则在于线程本身:

线程要素:

1、线程内核对象(thread kernel object)OS为系统中创建的每个线程都分配并初始化这种数据结构之一。在该数据结构中,包含一组对线程进行描述的属性。数据结构中还包含所谓的线程上下文。上下文是一个内存块,其中包含了CPU的寄存器集合。

2、程环境块(thread environment block,TEB)TEB是在用户模式(应用程序代码能够快速访问的地址空间)中分配和初始化的一个内存块。

3、用户模式栈用于存储传给方法的局部变量和实参。它还包含一个地址,指向当前方法返回时,线程接着应该从什么地方开始执行。默认情况下,Windows为每个线程的用户模式栈分配1MB的内存。

4、内核模式栈 应用程序代码向操作系统中的一个内核模式的函数传递实参时,还会使用内核模式栈。出于安全方面的原因,针对从用户模式的代码传递给内核的任何实参,Windows都会把它们从线程的用户
模式栈复制到线程的内核模式栈。一经复制,内核就可验证实参的值。由于应用程序代码不能访问内核模式栈,所以应用程序无法修改验证之后的实参值。OS内核代码将开始对复制的值进行处理。除此之外,内核会调用它自己内部的方法,并利用内核模式栈传递它自己的实参、存储函数的局部变量以及返回地址。

5、DLL线程附加(attach)和线程分离(detach)通知 Windows的一个策略,任何时候在进程中创建一个线程,都会调用那个进程中加载的所有DLL的DLLMain方法,并向该方法传递一个DLL_THREAD_ATTACH标志。类似地,任何时候一个线程终止,都会调用进程中的所有DLL的DLLMain方法,并向该方法传递一个DLL_THREAD_DETACH标志。
所有的DLL需要利用这些通知,为进程中创建/销毁的每个线程执行一些特殊的初始化或资源清理操作。

一个请求对应一个线程带来的问题是:

1、为每个请求创建一个新线程的开销很大,为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。

**活动线程:已启动并且尚未正常终止或中止的线程

因此在一个JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。

2、操作系统创建线程、切换线程状态、终结线程都要进行CPU调度,这是一个耗费时间和系统资源的事情。所以如果我们为每一个请求都单独创建一个线程,那么物理机的所有资源基本上都被操作系统创建线程、切换线程状态、销毁线程这些操作所占用,用于业务请求处理的资源反而减少了。

3、线程池作用:线程池通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,将处理请求的线程数量控制在一个范围,既保证后续的请求不会等待太长时间,又保证物理机将足够的资源用于请求处理本身。也就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。