今天在应用中用到了线程池,于是我就想web服务器肯定是用到了线程池,但是他们是怎样实现的呢?于是我就试着写了一个简单的模型
package httpserver;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
new Main().startSystem();
}
void startSystem(){
//创建一个固定大小的线程池
ExecutorService service = Executors.newFixedThreadPool(100);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(80);
Socket sock = null;
while((sock=serverSocket.accept()) != null){
//创建一个线程来处理请求
SocketHandleThread handleThread = new SocketHandleThread(sock);
//加入任务队列,等待执行
service.execute(handleThread);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SocketHandleThread extends Thread{
Socket socket ;
public SocketHandleThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
OutputStream out = socket.getOutputStream();
out.write("hello, request handled".getBytes());
} catch (IOException e) {
e.printStackTrace();
// terminate the thread by returnning
}
}
}
这样的代码是能够处理请求,但是总觉的是有问题的,我的理解是线城市应该是先创建好n个线程,然后要用的时候从池中取一个,然后将某些参数传给该线程,然后就执行了。
但我这里是每次有请求来都创建一个线程,这样肯定是不行的,太好资源了,不知道到底要怎样搞,再慢慢想吧!大家如果知道,请不吝赐教!在下感激不尽。
============2015年7-28=============
通过网上的文章和自己 的一些思考,终于弄明白了线程池的使用以及我之前的错误是在哪里 ,现在正确的实现方法。
package httpserver;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
int workerThreadCount = 5;
WorkerThread [] workerThread = new WorkerThread[workerThreadCount];
LinkedList<SocketTask> taskQueue = new LinkedList<SocketTask>();
public static void main(String[] args) throws Exception, IOException {
Main m = new Main();
m.startWorkerThreads();
m.startSystem();
}
void startWorkerThreads(){
for(int i = 0 ;i< workerThreadCount;i++){
workerThread[i] = new WorkerThread(taskQueue);
workerThread[i].start();
}
}
void startSystem(){
ExecutorService service = Executors.newFixedThreadPool(100);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8000);
Socket sock = null;
while((sock=serverSocket.accept()) != null){
//在向任务队列添加任务是需要同步,也就是要获取对象锁
synchronized (taskQueue){
SocketTask sockThread = new SocketTask(sock);
taskQueue.add(sockThread);
//唤醒一个等待中的worker线程来执行任务队列中的任务
taskQueue.notify();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//工作线程
class WorkerThread extends Thread{
LinkedList <SocketTask> taskQueue;
public WorkerThread(LinkedList<SocketTask> taskQueue) {
this.taskQueue = taskQueue;
}
public List<SocketTask> getTaskQueue() {
return taskQueue;
}
public void setTaskQueue(LinkedList<SocketTask> taskQueue) {
this.taskQueue = taskQueue;
}
@Override
public void run() {
while(true){
SocketTask task = null;
synchronized(taskQueue){
if(taskQueue.isEmpty()){
try {
taskQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
task = taskQueue.removeFirst();
}
}
if(task != null){
//直接调用run,并不是启动线程,只是执行了这个对象的run方法,和调用一个类的方法没区别
task.run();
}
}
}
}
//请注意这个类虽然继承了Thread,但是我们并不是把它作为一个线程来启动的,只是图方便使用了他的run方法,也可以自定义接口,JDK的 线程池也是这样搞的
class SocketTask extends Thread{
Socket socket ;
public SocketTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
OutputStream out = socket.getOutputStream();
out.write("hello, request handled , ^_^ ".getBytes());
out.flush();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
// terminate the thread by returnning
}
}
}
之前主要的错误是,我以为线程池执行需要的Runnable是一个线程对象,其实不是,你也可以定义为其他的接口,线程在调用时其实是直接调用的run()方法,所以没有启动线程(只有调用start()方法才会启动线程)。
上面的代码时自己实现了一个 简单的echo server,每次有客户端连接时,向客户端打印一条信息,这里的线程池是自己实现的,这种方式虽然在一定程度上可以正常工作但还是缺少很多功能,可以使用JDK提供的线程池,具有更多的功能,比如拒绝服务 以及更完善的排队队列。
问题1:我有遇到了一个问题,当我在创建ServerSocket时指定了,80端口,这是我本机的apache服务器也在运行,但并没有报错,然后请求被发到了apache服务器,这是什么原因的?求解答哦
补充:关于问题1答案已经找到了,这个问题很值得关注,请参考我的这篇文章 关于ip 0.0.0.0