[英]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
背后的想法是在启动时创建指定数量的线程,然后将任务分配给它们。 或者,消除每次创建线程的麻烦。 几天前我已经实现了它,这是我所做的。
这是一些排队方法
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.