`
hwy1782
  • 浏览: 153320 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论
阅读更多

转自:http://dev.csdn.net/htmls/45/45270.html



服务器程序利用线程技术响应客户请求已经司空见惯,可能您认为这样做效率已经很高,但您有没有想过优化一下使用线程的方法。该文章将向您介绍服务器程序如何利用线程池来优化性能并提供一个简单的线程池实现。
线程池的技术背景

在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的,本文将介绍的线程池技术同样符合这一思想。

目前,一些著名的大公司都特别看好这项技术,并早已经在他们的产品中应用该技术。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。

现在您是否也想在服务器程序应用该项技术?

线程池技术如何提高服务器程序的性能

我所提到服务器程序是指能够接受客户请求并能处理请求的程序,而不只是指那些接受网络客户请求的网络服务器程序。

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。但如果对多线程应用不当,会增加对单个任务的处理时间。可以举一个简单的例子:

假设在一台服务器完成一项任务的时间为T

T1 创建线程的时间
T2 在线程中执行任务的时间,包括线程间同步所需时间
T3 线程销毁的时间

显然T = T1+T2+T3。注意这是一个极度简化的假设。

可以看出T1,T3是多线程本身的带来的开销,我们渴望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在程序中频繁的创建或销毁线程,这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点(T1,T3),而不是优点(并发性)。

线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。

线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。在看一个例子:

假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。我们比较利用线程池技术和不利于线程池技术的服务器处理这些请求时所产生的线程总数。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目或者上限(以下简称线程池尺寸),而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池尺寸是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

这些都是假设,不能充分说明问题,下面我将讨论线程池的简单实现并对该程序进行对比测试,以说明线程技术优点及应用领域。

线程池的简单实现及对比测试

一般一个简单线程池至少包含下列组成部分。

线程池管理器(ThreadPoolManager):用于创建并管理线程池 
工作线程(WorkThread): 线程池中线程 
任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行。 
任务队列:用于存放没有处理的任务。提供一种缓冲机制。 
线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务创建线程池的部分代码如下:

  …
        //create threads
        synchronized(workThreadVector)
        {
            for(int j = 0; j < i; j++)
            {
                threadNum++;
               WorkThread workThread = new WorkThread(taskVector, threadNum);
                workThreadVector.addElement(workThread);
            }

        }
…
 


注意同步workThreadVector并没有降低效率,相反提高了效率,请参考Brian Goetz的文章。 销毁线程池的部分代码如下:

 …
        while(!workThreadVector.isEmpty())
        {
        if(debugLevel > 2)
         System.out.println("stop:"+(i));
         i++;
            try
            {
                WorkThread workThread = (WorkThread)workThreadVector.remove(0);
                workThread.closeThread();
                continue;
            }
            catch(Exception exception)
            {
                if(debugLevel > 2)
                    exception.printStackTrace();
            }
            break;
        }
   …
   
 


添加新任务的部分代码如下:

   …
        synchronized(taskVector)
        {
            taskVector.addElement(taskObj);
            taskVector.notifyAll();
        }
   …
   
 

工作线程是一个可以循环执行任务的线程,在没有任务时将等待。由于代码比较多在此不罗列.

任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。在文章结尾有相关代码的下载。

以上所描述的线程池结构很简单,一些复杂的线程池结构将不再此讨论。

在下载代码中有测试驱动程序(TestThreadPool),我利用这个测试程序的输出数据统计出下列测试结果。测试有两个参数要设置:

线程池中线程数,即线程池尺寸。 
要完成的任务数。 
分别将一个参数固定,另一个参数变动以考察两个参数所产生的不同结果。所用测试机器分别为普通PC机(Win2000 JDK1.3.1)和SUN服务器(Solaris Unix JDK1.3.1),机器配置在此不便指明。

表1:测试数据及对应结果 
线程池尺寸 任务数 没有应用线程池所用的时间(单位:毫秒,OS:win) 应用线程池所用的时间(单位:毫秒,OS:win) 没有应用线程池所用的时间(单位:毫秒,OS:Solaris) 应用线程池所用的时间(单位:毫秒,OS:Solaris) 
1 5000 3896 130 6513 327 
2 5000 3455 151 6221 659 
4 5000 3425 120 5448 433 
8 5000 3475 160 5769 1478 
16 5000 3505 211 5785 1970 
32 5000 3455 251 6403 875 
64 5000 3595 501 5182 1103 
128 5000 3515 881 5154 405 
256 5000 3495 3104 5502 1589 
512 5000 3425 5488 5667 1262 
16 1 20 0 22 3 
16 2 20 20 21 13 
16 4 20 10 27 10 
16 8 20 20 22 24 
16 16 30 20 29 48 
16 32 40 20 46 108 
16 64 60 20 72 199 
16 128 110 20 148 335 
16 256 201 20 252 132 
16 512 411 40 522 382 
16 1024 811 71 1233 610 
16 2048 1552 80 2045 135 
16 4096 2874 250 4828 787 




图1.线程池的尺寸的对服务器程序的性能影响 
根据以上统计数据可得出下图:



图2.任务数对服务器程序的冲击 

数据分析如下:

图1是改变线程池尺寸对服务器性能的影响,在该测试过程中,服务器的要完成的任务数固定为为5000。从图1中可以看出合理配置线程池尺寸对于大量任务处理的效率有非常明显的提高,但是一旦尺寸选择不合理(过大或过小)就会严重降低影响服务器性能。理论上"过小"将出现任务不能及时处理的情况,但在图表中显示出某些小尺寸的线程池表现很好,这是因为测试驱动中有很多线程同步开销,且这个开销相对于完成单个任务的时间是不能忽略的。"过大"则会出现线程间同步开销太大的问题,而且在线程间切换很耗CPU时间,在图表显示的很清楚。可见任何一个好技术,如果滥用都会造成灾难性后果。

图2是用不同数量的任务来冲击服务器程序,在该测试过程中,服务器线程池尺寸固定为16。可以看出线程池在处理少量任务时的优势不明显。所以线程池技术有一定的适应范围,关于适用范围将在后面讨论。但对于大量的任务的处理,线程池的优势表现非常卓越,服务器程序处理请求的时间虽然有波动,但是其平均值相对小多了。

值得注意的是测试方案中,统计任务的完成时间没有包含了创建线程池的时间。在实际线程池工作时,即利用线程池处理任务时,创建线程池的时间是不必计算在内的。

由于测试驱动程序有很多同步代码,特别是等待线程执行完毕的同步(代码中为sleepToWait(long l)方法的调用),这些代码降低了代码执行效率,这是测试驱动一个缺点,但这个测试驱动可以说明线程池相对于简单使用线程的优势。

关于高级线程池的探讨

简单线程池存在一些问题,比如如果有大量的客户要求服务器为其服务,但由于线程池的工作线程是有限的,服务器只能为部分客户服务,其它客户提交的任务,只能在任务队列中等待处理。一些系统设计人员可能会不满这种状况,因为他们对服务器程序的响应时间要求比较严格,所以在系统设计时可能会怀疑线程池技术的可行性,但是线程池有相应的解决方案。调整优化线程池尺寸是高级线程池要解决的一个问题。主要有下列解决方案:

方案一:动态增加工作线程

在一些高级线程池中一般提供一个可以动态改变的工作线程数目的功能,以适应突发性的请求。一旦请求变少了将逐步减少线程池中工作线程的数目。当然线程增加可以采用一种超前方式,即批量增加一批工作线程,而不是来一个请求才建立创建一个线程。批量创建是更加有效的方式。该方案还有应该限制线程池中工作线程数目的上限和下限。否则这种灵活的方式也就变成一种错误的方式或者灾难,因为频繁的创建线程或者短时间内产生大量的线程将会背离使用线程池原始初衷--减少创建线程的次数。

举例:Jini中的TaskManager,就是一个精巧线程池管理器,它是动态增加工作线程的。SQL Server采用单进程(Single Process)多线程(Multi-Thread)的系统结构,1024个数量的线程池,动态线程分配,理论上限32767。

方案二:优化工作线程数目

如果不想在线程池应用复杂的策略来保证工作线程数满足应用的要求,你就要根据统计学的原理来统计客户的请求数目,比如高峰时段平均一秒钟内有多少任务要求处理,并根据系统的承受能力及客户的忍受能力来平衡估计一个合理的线程池尺寸。线程池的尺寸确实很难确定,所以有时干脆用经验值。

举例:在MTS中线程池的尺寸固定为100。

方案三:一个服务器提供多个线程池

在一些复杂的系统结构会采用这个方案。这样可以根据不同任务或者任务优先级来采用不同线程池处理。

举例:COM+用到了多个线程池。

这三种方案各有优缺点。在不同应用中可能采用不同的方案或者干脆组合这三种方案来解决实际问题。

线程池技术适用范围及应注意的问题

下面是我总结的一些线程池应用范围,可能是不全面的。

线程池的应用范围:

需要大量的线程来完成任务,且完成任务的时间比较短。
WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。
但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

对性能要求苛刻的应用,比如要求服务器迅速相应客户请求。 
接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。
突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,并出现"OutOfMemory"的错误。 
结束语

本文只是简单介绍线程池技术。可以看出线程池技术对于服务器程序的性能改善是显著的。线程池技术在服务器领域有着广泛的应用前景。希望这项技术能够应用到您的多线程服务程序中。

分享到:
评论

相关推荐

    线程池技术在并发服务器中的应用

    线程池技术在并发服务器中的应用 线程池技术是指在服务器程序中,预先创建一组线程,供客户端请求时使用,以减少创建和销毁线程的开销,提高系统的处理能力。这种技术可以最大程度地利用系统的资源,消除系统因频繁...

    线程池技术研究与应用

    ### 线程池技术研究与应用 #### 摘要 线程池技术作为一种有效管理线程资源的方法,在服务器软件开发中具有重要的地位。它能够显著减少线程创建和销毁带来的开销,并有助于解决系统资源不足的问题。本文将详细介绍...

    基于Java线程池技术的数据爬虫设计与实现.pdf

    本文所提及的基于Java线程池技术的数据爬虫设计与实现,不仅涉及到了数据爬虫的原理和架构,还包括了多线程编程的知识点,以及线程池技术在数据爬虫中的具体应用。 首先,数据爬虫的基本原理是模拟用户的点击行为,...

    线程池技术在网络游戏服务器中的应用

    "线程池技术在网络游戏服务器中的应用" 线程池技术是网络游戏服务器中的一种重要技术,它可以提高服务器的性能,减少系统资源的开销。在网络游戏服务器中,线程池技术可以用于处理大量的数据包,以提高服务器的...

    基于Java线程池技术实现Knock Knock游戏项目.zip

    基于Java线程池技术实现Knock Knock游戏项目.zip 基于Java线程池技术实现Knock Knock游戏项目.zip 基于Java线程池技术实现Knock Knock游戏项目.zip 基于Java线程池技术实现Knock Knock游戏项目.zip 基于Java线程池...

    java技术学习-基于Java线程池技术实现Knock Knock游戏项目(包含服务端、客户端两部分)

    java技术学习——基于Java线程池技术实现Knock Knock游戏项目(包含服务端、客户端两部分) java技术学习——基于Java线程池技术实现Knock Knock游戏项目(包含服务端、客户端两部分) java技术学习——基于Java...

    _基于线程池技术的文件传输模型的改进

    ### 基于线程池技术的文件传输模型的改进 #### 摘要 本文探讨了一种基于线程池技术改进的文件传输模型。通过分析文件传输的特点以及线程池技术的优势,提出了一种新的文件传输方法,旨在提高文件传输效率并优化系统...

    vc 线程池技术源码   

    线程池技术是多线程编程中的一个重要概念,它在现代软件开发中被广泛应用,特别是在高性能服务器、并发处理和异步编程中。VC++(Visual C++)作为Microsoft的C++开发工具,提供了对线程池的支持,让我们可以通过标准...

    基于对象池模式的自适应线程池技术

    ### 基于对象池模式的自适应线程池技术 #### 一、引言 在现代软件开发中,特别是对于高性能服务器应用而言,优化线程管理是提升系统响应能力和资源利用率的关键因素之一。传统的线程池技术虽然能显著减少线程创建...

    线程池技术研究与应用.pdf

    线程池技术的核心思想是预先创建一定数量的线程放到池中,这些线程可以被重复使用,从而避免了频繁创建和销毁线程带来的开销。当有任务到来时,线程池会将任务分配给池中的线程去执行,这样可以减少系统在创建和销毁...

    线程池技术在J2ME网络通信中的应用研究

    ### 线程池技术在J2ME网络通信中的应用研究 #### 引言与背景 随着嵌入式设备及移动通信技术的迅速发展,尤其是智能手机和平板电脑的普及,移动设备的应用范围日益广泛,从基本的通讯工具演变为集信息获取、娱乐等...

    线程池模板 (可以让你快速地使用线程池技术) 帮助你学习线程池

    线程池模板 (可以让你快速地使用线程池技术) 帮助你学习线程池

    面向Socket基于线程池技术的java聊天室程序下载(含源代码)

    本程序采用面向Socket编程和线程池技术,这两种技术在Java网络编程中扮演着核心角色。 首先,Socket编程是TCP/IP协议族的一部分,它提供了进程间的网络通信能力。在Java中,`java.net.Socket`类和`java.net....

    论文研究-线程池技术研究与应用.pdf

    线程池技术是现代软件设计中的一项关键技术,它主要用来解决线程创建和销毁的开销问题,同时也能有效解决系统资源不足的问题。服务器软件在处理多任务时,频繁地创建和销毁线程会消耗大量的系统资源并降低程序效率。...

    线程池技术在J2ME网络通信中的应用研究.

    ### 线程池技术在J2ME网络通信中的应用研究 #### 一、引言 随着嵌入式设备的迅速发展,特别是智能手机和平板电脑等手持移动设备的普及,移动应用开发的需求日益增长。Java作为一种重要的编程语言,在移动设备应用...

    Java线程池技术详解

    【Java线程池技术详解】 线程池是Java并发编程中的一个重要概念,它是一种线程使用模式,旨在优化线程的管理和使用,提高系统资源的利用率。线程池的引入是为了应对频繁创建和销毁线程所带来的性能开销,因为创建和...

    采用boost内存数据库技术和线程池技术开发的内存池技术,支持内存回收,碎片合并

    接下来是线程池技术。线程池是一种管理线程资源的有效方式,它可以预先创建一组线程,待有任务需要执行时,从线程池中获取空闲线程进行任务处理,完成后归还给线程池。这种方式避免了频繁地创建和销毁线程,减少了...

Global site tag (gtag.js) - Google Analytics