zoukankan      html  css  js  c++  java
  • 多任务处理:线程池

    线程池

     每个新线程都会消耗系统资源:创建一个线程将占用CPU周期,而且每个线程都自己的数据结构(如,栈)也要消耗系统内存。另外,当一个线程阻塞(block)时,JVM将保存其状态,选择另外一个线程运行,并在上下文转换(context switch)时恢复阻塞线程的状态。随着线程数的增加,线程将消耗越来越多的系统资源。这将最终导致系统花费更多的时间来处理上下文转换和线程管理,更少的时间来对连接进行服务。那种情况下,加入一个额外的线程实际上可能增加客户端总服务时间。

     我们可以通过限制总线程数并重复使用线程来避免这个问题。与为每个连接创建一个新的线程不同,服务器在启动时创建一个由固定数量线程组成的线程池(thread pool)。当一个新的客户端连接请求传入服务器,它将交给线程池中的一个线程处理。当该线程处理完这个客户端后,又返回线程池,并为下一次请求处理做好准备。如果连接请求到达服务器时,线程池中的所有线程都已经被占用,它们则在一个队列中等待,直到有空闲的线程可用。

     与一客户一线程服务器一样,线程池服务器首先创建一个ServerSocket实例。然后创建N个线程,每个线程都反复循环,从(共享的)ServerSocket实例接收客户端连接。当多个线程同时调用同一个ServerSocket实例的accept()方法时,它们都将阻塞等待,直到一个新的连接成功建立。然后系统选择一个线程,新建立的连接对应的Socket实例则只在选中的线程中返回。其他线程则继续阻塞,直到成功建立下一个连接和选中另一个幸运的线程。

    由于线程池中的所有线程都反复循环,一个接一个地处理客户端连接,线程池服务器的行为就像是一组迭代服务器。与一客户一线程服务器不同,线程池中的线程在完成对一个客户端的服务后并不终止,相反,它又重新开始在accept()方法上阻塞等待。TCPEchoServerPool.java中演示了一个线程池的例子。

    TCPEchoServerPool.java

    0 import java.io.IOException;

    1 import java.net.ServerSocket;

    2 import java.net.Socket;

    3 import java.util.logging.Level;

    4 import java.util.logging.Logger;

    5

    6 public class TCPEchoServerPool {

    7

    8 public static void main(String[] args) throws

    IOException {

    9

    10 if (args.length != 2) { // Test for correct # of args

    11 throw new IllegalArgumentException("Parameter(s):

    <Port> <Threads>");

    12 }

    13

    14 int echoServPort = Integer.parseInt(args[0]); // Server

    port

    15 int threadPoolSize = Integer.parseInt(args[1]);

    16

    17 // Create a server socket to accept client connection

    requests

    18 final ServerSocket servSock = new

    ServerSocket(echoServPort);

    19

    20 final Logger logger = Logger.getLogger("practical");

    21

    22 // Spawn a fixed number of threads to service clients

    23 for (int i = 0; i < threadPoolSize; i++) {

    24 Thread thread = new Thread() {

    25 public void run() {

    26 while (true) {

    27 try {

    28 Socket clntSock = servSock.accept(); // Wait for a

    connection

    29 EchoProtocol.handleEchoClient(clntSock, logger); //

    Handle it

    30 } catch (IOException ex) {

    31 logger.log(Level.WARNING, "Client accept failed", ex);

    32 }

    33 }

    34 }

    35 };

    36 thread.start();

    37 logger.info("Created and started Thread = " +

    thread.getName());

    38 }

    39 }

    40 }

     

    TCPEchoServerPool.java

    1.设置:第10-20

    要侦听的端口号和线程的数量都作为参数传递给main()。对参数进行解析后再创建

    ServerSocket Logger实例。注意要它们都必须声明为常量(final),因为它们将在下面创

    建的匿名类中引用。

    2.创建并启动threadPoolSize个新线程:第23-38

    循环的每一次迭代都会创建一个继承于Thread的匿名类的实例。当调用该实例的start()方法时,这个线程就会执行该匿名类的run()方法。run()方法将反复循环,接受客户端的连接请求,并传递给EchoProtocol进行处理。

    接受连接请求:第28

     由于有N个不同线程在执行同一个循环,那么最多有N个线程在servSockaccept()方法上阻塞等待传入的连接请求。对于任何一个连接,系统保证了只要一个线程能够获得其对应的Socket。在一个客户端连接被创建时,如果没有线程在accept()方法上阻塞等待(即,所有线程都在忙着为其他连接服务),系统则将新的连接排列在一个队列中,直到下一次调accept()方法(见第6.4.1节)。

    将客户端套接字传递给EchoProtocol.handleEchoClient()方法:第29 

    handleEchoClient()方法中封装了协议的详细内容。该方法在连接处理完成后将相关信息写入日志,处理过程中遇到的异常也将写入日志。

    处理accept()方法抛出的异常:第31

    由于线程的重复使用,线程池的方法只需要付出创建N次线程的系统开销,而与客户端连接总数无关。由于可以控制最大并发执行线程数,我们就可以控制线程的调度和资源开销。当然,如果我们创建的线程太少,客户端还是有可能等很长时间才获得服务,因此,线程池的大小需要根据负载情况进行调整,以使客户端连接的时间最短。理想的情况是有一个调度工具,可以在系统负载增加时扩展线程池的大小(低于大小上限),负载较轻时缩减线程池的大小。Java恰好就有这种工具,我们将在下一节进行介绍。

    相关下载:

    Java_TCPIP_Socket编程(doc)

    http://download.csdn.net/detail/undoner/4940239

     

    文献来源:

    UNDONER(小杰博客) :http://blog.csdn.net/undoner

    LSOFT.CN(琅软中国) :http://www.lsoft.cn

  • 相关阅读:
    c语言结构体数组引用
    c语言结构体数组定义的三种方式
    如何为SAP WebIDE开发扩展(Extension),并部署到SAP云平台上
    SAP SRM ABAP Webdynpro和CFCA usb key集成的一个原型开发
    使用SAP API portal进行SAP SuccessFactors的API测试
    SAP UI5应用里的页面路由处理
    在SAP WebIDE Database Explorer里操作hdi实例
    如何使用SAP事务码SAT进行UI应用的性能分析
    使用SAP WebIDE进行SAP Cloud Platform Business Application开发
    SAP CRM WebClient UI ON_NEW_FOCUS的用途
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301069.html
Copyright © 2011-2022 走看看