线程池化服务器

时间:2020-01-09 10:36:13  来源:igfitidea点击:

本文介绍了用Java实现的简单线程池服务器。该代码基于多线程服务器上文本中描述的多线程服务器。主要区别在于服务器循环。而不是为每个传入的连接启动新线程,该连接被包装在" Runnable"中,并传递给具有固定线程数的线程池。 Runnable被保存在线程池的队列中。当线程池中的线程处于空闲状态时,它将从队列中获取" Runnable"并执行它。

注意:线程池一文中将详细讨论线程池。

这是线程池版本中服务器循环的外观(完整的代码显示在本文的底部):

while(! isStopped()){
         Socket clientSocket = null;
         try {
             clientSocket = this.serverSocket.accept();
         } catch (IOException e) {
             if(isStopped()) {
                 System.out.println("Server Stopped.") ;
                 break;
             }
             throw new RuntimeException(
                "Error accepting client connection", e);
         }
        
            this.threadPool.execute(
            new WorkerRunnable(clientSocket, "Thread Pooled Server"));
        
     }

从多线程服务器到此处的循环中唯一的变化是粗体代码:

this.threadPool.execute(
   new WorkerRunnable(clientSocket, "Thread Pooled Server"));

当池中的线程变为空闲时,而不是为每个传入的连接启动新线程,而是将" WorkerRunnable"传递给线程池以执行该线程。

这是WorkerRunnable类的代码,该代码传递给工作线程构造函数:

package servers;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.Socket;

public class WorkerRunnable implements Runnable{

    protected Socket clientSocket = null;
    protected String serverText   = null;

    public WorkerRunnable(Socket clientSocket, String serverText) {
        this.clientSocket = clientSocket;
        this.serverText   = serverText;
    }

    public void run() {
        try {
            InputStream input  = clientSocket.getInputStream();
            OutputStream output = clientSocket.getOutputStream();
            long time = System.currentTimeMillis();
            output.write(("HTTP/1.1 200 OK\n\nWorkerRunnable: " +
this.serverText + " - " +
time +
"").getBytes());
            output.close();
            input.close();
            System.out.println("Request processed: " + time);
        } catch (IOException e) {
            //report exception somewhere.
            e.printStackTrace();
        }
    }
}

线程池服务器的优势

与多线程服务器相比,线程池服务器的优势在于我们可以控制同时运行的最大线程数。这具有一定的优势。

首先,如果请求需要大量的CPU时间,RAM或者网络带宽,则如果同时处理许多请求,这可能会降低服务器的速度。例如,如果内存消耗导致服务器将内存换入和换出磁盘,这将导致严重的性能损失。通过控制线程的最大数量,可以将资源耗尽的风险降到最低,这既是由于限制了处理请求所占用的内存,又是由于线程的限制和重用。每个线程也占用一定数量的内存,仅代表线程本身。

此外,同时执行许多请求将减慢所有已处理的请求的速度。例如,如果我们同时处理1.000个请求,而每个请求花费1秒钟,则所有请求将花费1.000秒才能完成。如果改为将请求排队,并一次处理10个请求,则前10个请求将在10秒后完成,接下来的10个请求将在20秒后完成,依此类推。只有最后10个请求将在1.000秒后完成。这样可以为客户提供更好的服务。

线程池服务器代码

这是ThreadPooledServer的完整代码:

package servers;

import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPooledServer implements Runnable{

    protected int          serverPort   = 8080;
    protected ServerSocket serverSocket = null;
    protected boolean      isStopped    = false;
    protected Thread       runningThread= null;
    protected ExecutorService threadPool =
        Executors.newFixedThreadPool(10);

    public ThreadPooledServer(int port){
        this.serverPort = port;
    }

    public void run(){
        synchronized(this){
            this.runningThread = Thread.currentThread();
        }
        openServerSocket();
        while(! isStopped()){
            Socket clientSocket = null;
            try {
                clientSocket = this.serverSocket.accept();
            } catch (IOException e) {
                if(isStopped()) {
                    System.out.println("Server Stopped.") ;
                    break;
                }
                throw new RuntimeException(
                    "Error accepting client connection", e);
            }
            this.threadPool.execute(
                new WorkerRunnable(clientSocket,
                    "Thread Pooled Server"));
        }
        this.threadPool.shutdown();
        System.out.println("Server Stopped.") ;
    }

    private synchronized boolean isStopped() {
        return this.isStopped;
    }

    public synchronized void stop(){
        this.isStopped = true;
        try {
            this.serverSocket.close();
        } catch (IOException e) {
            throw new RuntimeException("Error closing server", e);
        }
    }

    private void openServerSocket() {
        try {
            this.serverSocket = new ServerSocket(this.serverPort);
        } catch (IOException e) {
            throw new RuntimeException("Cannot open port 8080", e);
        }
    }
}

这是运行它的代码:

ThreadPooledServer server = new ThreadPooledServer(9000);
new Thread(server).start();

try {
    Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("Stopping Server");
server.stop();

服务器运行时,我们可以使用普通的Web浏览器访问它。使用地址http:// localhost:9000 /