精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2008-04-05
JAVA后台程序设计及UTIL.CONCURRENT包的应用
何 恐 摘要 : 在很多软件项目中,JAVA语言常常被用来开发后台服务程序。线程池技术是提高这类程序性能的一个重要手段。在实践中,该技术已经被广泛的使用。本文首先 对设计后台服务程序通常需要考虑的问题进行了基本的论述,随后介绍了JAVA线程池的原理、使用和其他一些相关问题,最后对功能强大的JAVA开放源码线 程池包util.concurrent 在实际编程中的应用进行了详细介绍。 关键字: JAVA;线程池;后台服务程序;util.concurrent 1 引言 在软件项目开发中,许多后台服务程序的处理动作流程都具有一个相同点,就是:接受客户端发来的请求,对请求进行一些相关的处理,最后将处理结果返回给客户 端。这些请求的来源和方式可能会各不相同,但是它们常常都有一个共同点:数量巨大,处理时间短。这类服务器在实际应用中具有较大的普遍性,如web服务 器,短信服务器,DNS服务器等等。因此,研究如何提高此类后台程序的性能,如何保证服务器的稳定性以及安全性都具有重要的实用价值。 2 后台服务程序设计 2.1 关于设计原型 构建服务器应用程序的一个简单的模型是:启动一个无限循环,循环里放一个监听线程监听某个地址端口。每当一个请求到达就创建一个新线程,然后新线程为请求服务,监听线程返回继续监听。 简单举例如下: import java.net.*; public class MyServer extends Thread{ public void run(){ try{ ServerSocket server=null; Socket clientconnection=null; server = new ServerSocket(8008);//监听某地址端口对 while(true){进入无限循环 clientconnection =server.accept();//收取请求 new ServeRequest(clientconnection).start();//启动一个新服务线程进行服务 …… } }catch(Exception e){ System.err.println("Unable to start serve listen:"+e.getMessage()); e.printStackTrace(); } } } 实际上,这只是个简单的原型,如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显。 首先,为每个请求创建一个新线程的开销很大,为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源, 往往有时候要比花在处理实际的用户请求的时间和资源更多。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提 高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数。这样综合看来,系统的性能瓶颈就在于线程的创建开销。 其次,除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻运行的处理 线程数目,以防止服务器被“压死”的情况发生。所以在设计后台程序的时候,一般需要提前根据服务器的内存、CPU等硬件情况设定一个线程数量的上限值。 如果创建和销毁线程的时间相对于服务时间占用的比例较大,那末假设在一个较短的时间内有成千上万的请求到达,想象一下,服务器的时间和资源将会大量的花在 创建和销毁线程上,而真正用于处理请求的时间却相对较少,这种情况下,服务器性能瓶颈就在于创建和销毁线程的时间。按照这个模型写一个简单的程序测试一下 即可看出,由于篇幅关系,此处略。如果把(服务时间/创建和销毁线程的时间)作为衡量服务器性能的一个参数,那末这个比值越大,服务器的性能就越高。 应此,解决此类问题的实质就是尽量减少创建和销毁线程的时间,把服务器的资源尽可能多地用到处理请求上来,从而发挥多线程的优点(并发),避免多线程的缺点(创建和销毁的时空开销)。 线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时 线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也 就是当请求的数目超过某个阈值时,就强制其它任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。 3 JAVA线程池原理 3.1 原理以及实现 在实践中,关于线程池的实现常常有不同的方法,但是它们的基本思路大都是相似的:服务器预先存放一定数目的“热”的线程,并发程序需要使用线程的时候,从 服务器取用一条已经创建好的线程(如果线程池为空则等待),使用该线程对请求服务,使用结束后,该线程并不删除,而是返回线程池中,以备复用,这样可以避 免对每一个请求都生成和删除线程的昂贵操作。 一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作 用是提供一种缓冲机制,将没有处理的任务放在任务队列中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执 行状态等,工作线程通过该接口调度任务的执行。下面的代码实现了创建一个线程池: public class ThreadPool { private Stack threadpool = new Stack(); private int poolSize; private int currSize=0; public void setSize(int n) { poolSize = n; } public void run() { for(int i=0;i (发帖时间:2003-11-30 11:55:56) ---岑心 J 回复(1): Title: * Description: 负责初始化线程池以及启动服务器 * Copyright: Copyright (c) 2003 * Company: * @author not attributable * @version 1.0 */ public class MainServer { //初始化常量 public static final int MAX_CLIENT=100; //系统最大同时服务客户数 //初始化线程池 public static final PooledExecutor pool = new PooledExecutor(new BoundedBuffer(10), MAX_CLIENT); //chanel容量为10, //在这里为线程池初始化了一个 //长度为10的任务缓冲队列。 public MainServer() { //设置线程池运行参数 pool.setMinimumPoolSize(5); //设置线程池初始容量为5个线程 pool.discardOldestWhenBlocked();//对于超出队列的请求,使用了抛弃策略。 pool.createThreads(2); //在线程池启动的时候,初始化了具有一定生命周期的2个“热”线程 } public static void main(String[] args) { MainServer MainServer1 = new MainServer(); new HTTPListener().start();//启动服务器监听和处理线程 new manageServer().start();//启动管理线程 } } 类HTTPListener import java.net.*; /** * Title: * Description: 负责监听端口以及将任务交给线程池处理 * Copyright: Copyright (c) 2003 * Company: * @author not attributable * @version 1.0 */ public class HTTPListener extends Thread{ public HTTPListener() { } public void run(){ try{ ServerSocket server=null; Socket clientconnection=null; server = new ServerSocket(8008);//服务套接字监听某地址端口对 while(true){//无限循环 clientconnection =server.accept(); System.out.println("Client connected in!"); //使用线程池启动服务 MainServer.pool.execute(new HTTPRequest(clientconnection));//如果收到一个请求,则从线程池中取一个线程进行服务,任务完成后,该线程自动返还线程池 } }catch(Exception e){ System.err.println("Unable to start serve listen:"+e.getMessage()); e.printStackTrace(); } } } 关于util.concurrent工具包就有选择的介绍到这,更详细的信息可以阅读这些java源代码的API文档。Doug Lea是个很具有“open”精神的作者,他将util.concurrent工具包的java源代码全部公布出来,有兴趣的读者可以下载这些源代码并细 细品味。 5 结束语 以上内容介绍了线程池基本原理以及设计后台服务程序应考虑到的问题,并结合实例详细介绍了重要的多线程开发工具包util.concurrent的构架和使用。结合使用已有完善的开发包,后端服务程序的开发周期将大大缩短,同时程序性能也有了保障。 参考文献 [1] Chad Darby,etc. 《Beginning Java Networking》. 电子工业出版社. 2002年3月. [2] util.concurrent 说明文件 http://gee.cs.oswego.edu/dl/cpjslides/util.pdf [3] 幸勇.线程池的介绍及简单实现.http://www-900.ibm.com/developerWorks/cn/java/l-threadPool/index.shtml 2002年8月 [4]BrianGoetz. 我的线程到哪里去了http://www-900.cn.ibm.com/developerworks/cn/java/j-jtp0924/index.shtml 2002 年 12 月 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
浏览 3243 次