繁体   English   中英

Java中Web服务器中的线程池

[英]Thread pool in a web server in Java

我有我的多线程Web服务器,现在我希望实现一个线程池,但是即使在查看它之后,我也无法在我的代码中找到它:(

有人可以帮我更好吗? 我真的需要了解如何在这里使用阅读的内容,因为我看不到连接及其工作原理。

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;


public class WebServer {

    static class RequisicaoRunnable implements Runnable {

        private Socket socket;

        RequisicaoRunnable(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                //System.out.println("connection from " + socket.getInetAddress().getHostName());
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //System.out.println("READING SOCKET...");
                String str = in.readLine();
                String[] arr = str.split(" ");
                if (arr != null && arr.length > 2) {
                    while(!str.equals("")) {
                        //System.out.println(str);
                        str = in.readLine();
                    }
                    if (arr[0].equals("GET")) {
                        //System.out.println("REQUESTED RESOURCE: " + arr[1]);
                        String nomeArquivo = arr[1];
                        if (arr[1].startsWith("/")) {
                            nomeArquivo = nomeArquivo.substring(1);
                        }
                        if (nomeArquivo.equals("")) {
                            nomeArquivo = "index.html";
                        }
                        File f = new File(nomeArquivo);
                        if (f.exists()) {
                            FileInputStream fin = new FileInputStream(f);
                            socket.getOutputStream().write("HTTP/1.0 200 OK\n\n".getBytes());
                            byte[] buffer = new byte[1024];
                            int lidos;
                            do {
                                lidos = fin.read(buffer);
                                if (lidos > 0) {
                                    socket.getOutputStream().write(buffer, 0, lidos);
                                }
                            } while (lidos > 0);
                            fin.close();
                        } else {
                            socket.getOutputStream().write("HTTP/1.0 404 Not Found\n\n".getBytes());
                            socket.getOutputStream().write("<html><body>HTTP/1.0 404 File Not Found</body></html>\n\n".getBytes());
                        }
                    } else {
                        socket.getOutputStream().write("HTTP/1.0 501 Not Implemented\n\n".getBytes());
                    }
                }
                socket.close();
            } catch (IOException e) { }
        }
    }

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("waiting connections....");
        while (true) {
            Socket socket = serverSocket.accept();
            RequisicaoRunnable req = new RequisicaoRunnable(socket);
            new Thread(req).start();
        }
    }

}

JDK可能是启动Executor或ExecutorService的理想场所。 阅读材料: http : //docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html

我认为其中的示例相当完整,但这是使用您发布的代码的示例:

public static void main(String[] args) throws IOException {




ServerSocket serverSocket = new ServerSocket(8080);
    System.out.println("waiting connections....");

    ExecutorService pool = Executors.newCachedThreadPool();

   while (true) {
        Socket socket = serverSocket.accept();
        RequisicaoRunnable req = new RequisicaoRunnable(socket);
        pool.execute(req);
    }
}

我们创建由缓存线程池支持的执行程序服务。 您可以通过更改从执行程序获得的执行程序服务的类型,将其交换为所需的任何类型的池: http : //docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors。 html

在我给出的示例中,我们使用了一个高速缓存的线程池,该线程池应根据需要创建新线程,但在可用时重新使用旧线程(完成所有正在执行的操作)。 如果查看该类中提供的方法,则可以创建由各种类型的线程池支持的Executor服务,例如单线程,固定数量的线程等。

上面的示例应按原样工作,但是如果您想更改线程池的工作方式,请尝试其他线程池类型。 缓存的线程池将意味着每个连接将立即得到服务,但是它可以创建无限数量的线程。

另一方面,如果您希望执行程序按照fge的建议使用阻塞队列,则可以尝试使用固定线程池:

Executors.newFixedThreadPool(x)

您可以免费获得阻塞队列。

例如,您可以使用BlockingQueue 这是生产者/消费者方案的基础。

在您的情况下:

  • 生产者持有服务器套接字; 它接受新的客户端套接字并将客户端套接字推送到队列中;
  • 使用者从队列中获取客户端套接字并处理请求。

最重要的是,您还可以使用有队列。 您可以尝试将新的客户端套接字推送到队列中; 如果队列已满,则可以默认为“不能做”的使用者。

场景很多。 没有一个答案。

Thread pool背后的想法是在启动时创建指定数量的线程,然后将任务分配给它们。 或者,消除每次创建线程的麻烦。 几天前我已经实现了它,这是我所做的。

  1. 在启动时创建一些线程,它们共享一个请求队列
  2. 线程一直在寻找队列,并且当请求到达时,其中一个线程调度该请求并执行操作
  3. 队列将同步3。

这是一些排队方法

Queue#add();    //add the socket at the end
Queue#removeFront();//remove socket
Queue#isEmpty();//boolean if queue is empty
Queue#size(); //return size of queue
Queue#getMaxSize();//get maximum allowed size for queue

您的请求处理可运行

public class Processor implements Runnable {

    private Queue<Socket> requests;
    private boolean shut;

    Processor(Queue<Socket> requests) {
        this.requests = requests;
        shut = false;
    }

    @Override
    public void run() {
        while(!shut) {
            if(requests.isEmpty()) {
                try{
                    Thread.sleep(#rendomeTimemill);
                } catch(InterruptedException e){}
            }else {
                Socket skt = Queue.removeFront();
                try {
                    //System.out.println("processing request from " + socket.getInetAddress().getHostName());
                    //do you want
                } catch (Exception e) {
                } finally {
                    if(skt != null) {
                        try{ skt.close(); skt = null; } catch(IOException ex){}

                    }
                }
            }
        }
    }
    public void stopNow() {
        shut = true;
        Thread.interrupt();
    }
}

在您的主线程中创建一个队列来放置请求

//start your server socket
Queue<Socket> requests = new Queue<Socket>();

启动工作线程池

Precessor []workers = new Processor[NUM_WORKER];
for(int i=0;i<NUM_WORKER; i++) {
    worker[i] = new Processor(requests);
    Thread th = new Thread(worker[i]);
    th.strat();
}

要求聆听

//while loope that run forever
// accept socket
    if(requests.size() == requests.getMaxSize()) {
        socket.getOutputStream().write("HTTP/1.0 505 Error\n\n".getBytes());
        socket.getOutputStream().write("<html><body>Try again</body></html>\n\n".getBytes());
        socket.close();
    } else {
            requests.add(socket);
    }

当您想关闭服务器时

for(int i=0;i<NUM_WORKER; i++) {
    worker[i].stopNow();
}

注意:我所关心的不是HTTP标头,所以不是特定的,但是您必须实现完整的HTTP标头,例如Content-type,Content-length等。

好的,这个想法很简单。 您的主循环当前会创建一个新的RequisicaoRunnable对象和一个新的线程,以在每次从客户端获得连接时运行该对象。 线程池背后的想法是避免每次都创建新的线程。

在最简单的线程池版本中,您将创建一个阻塞队列,并在进入主循环之前创建并启动固定数量的工作线程 主循环看起来几乎与您现在所拥有的循环完全相同,但是它不会启动线程来运行每个新的RequisicaoRunnable,而是只会将新对象添加到队列中。

您的工作线程是相同的:while(!shutdownHasBeenRequested()){RequisicaoRunnable requisicaoRunnable = getATaskFromTheQueue(); requisicaoRunnable.run(); }

这样,池中的下一个可用线程将执行(处理)每个新任务(客户端)。

如果这是一项家庭作业,那么您将非常想实现我所描述的内容,并根据需要填写一些细节。

如果不是家庭作业,请考虑改用java.util.concurrent.ThreadPoolExcecutor()。 当有一个非常好的轮毂等待使用时,重新设计轮毂毫无意义。


编辑:正如fge所说,一种改进是当新连接的传入速度快于您处理它们的速度时,可以发送快速的“抱歉,稍后重试”响应。 当队列中有太多待处理的连接时(即,当您达到BoundedQueue的限制时),这就是您知道要纾困并发送“稍后重试”响应的时候。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM