线程池:就是为了解决一客户端一线程的过度资源消耗问题,而在预先定义好线程的数量,而当有新的请求接入时,就将这个新的请求接入封装成一个task丢到线程池中处理,在这种情况下,资源的使用是可控的。
同样,以一个客户端发送消息,服务器接收并且返回消息的例子作为分析。
思路:
Server端:
1. server端使用ServerSocket的一个实例,绑定端口并且监听,
2. 创建一个线程池,指定线程池属性
3. ServerSocket实例将接收到的每个socket单独封装成一个task,
4. 将task丢到线程池中处理
Client端:
1. 新建一个Socket实例,并且连接到server,(建议将这个Socket封装成一个单独的类),
2. 获取Socket的输入流和输出流,收发信息
Server端代码:三部分组成,ExecutorPoolServer.java,主函数入口
;ExecutePoolServerHandler.java,用于封装线程池的类;BIOSocketHandler.java;负责到客户端的Soket连接;
代码:
ExecutePoolServer.java
package learningNote.IO;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ExecutePoolServer {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
int port=8090;
ServerSocket ss=new ServerSocket(port);
ExecutePoolServerHandler esph=new ExecutePoolServerHandler();//创建线程池
while(true){
System.out.println(esph.activtedThreadCount());//查看这个线程池上面的活跃线程数
Socket s=ss.accept();
esph.execute(new BIOSocketHandler(s));//将新的请求连接放入线程池中
}
}
}
ExecutePoolServerHandler.java
package learningNote.IO;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExecutePoolServerHandler{
private ExecutorService es;
public ExecutePoolServerHandler() {
// TODO Auto-generated constructor stub
es=new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MICROSECONDS, new ArrayBlockingQueue<Runnable>(10));//这个构造函数的说明可以查阅:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html#ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue)
}
public void execute(Runnable task){
es.execute(task);//这里的参数必须是Runnable类型的实例,因为jvm创建的线程会根据此来调用run()方法
}
public int activtedThreadCount(){
return Thread.activeCount();
}
}
BIOSocketHandler.java
package learningNote.IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class BIOSocketHandler implements Runnable{
private Socket s;
public BIOSocketHandler(Socket s) {
// TODO Auto-generated constructor stub
this.s = s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try (BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));//获取socket的输入流
PrintWriter out=new PrintWriter(s.getOutputStream(),true);//获取socket的输出流,并且设置自动刷新缓冲区
){
//读取输入消息并打印
String message=" ";
while(true){
//读取输入消息
while(true){
String temp=in.readLine();
if(temp.equals("ok")){
break;
}
message+=temp+"\n";
}
//打印收到的消息
System.out.print(message);
//返回消息
String now=new java.util.Date().toString();
out.println(now+" "+message+"ok");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.exit(1);
}
}
}
- 当线程池中的线程(指定的最大线程)已经全部用完之后,新的task会被放到ArrayBlockingQueue,这时一个顺序队列,属于先进先出的一种能够算法.,就是最先进入的元素也会最先出队。
- 注意到server 端socket会一直存在直到client端的socket被关闭,也就是说,会导致线程的一直占用
- 注意InputStream中的read方法,这个方法本身也会引起阻塞因为它不会立即被返回直到:数据可用(input data is available ,),数据读取完毕(end of the file is detected ),或者抛出错误(throw a exception)。..前两者的情况不太清楚…因为这样岂不是会被返回两次??
OutputStream中的write方法,也会造成阻塞,(这很大程度上与tcp/ip缓冲区以及网络状况有关)..
Client端代码:
ExecutePoolClient .java
package learningNote.IO;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ExecutePoolClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
Socket s=new Socket("127.0.0.1",8090);
new Thread(new BIOClientHandler(s)).start();
}
}
BIOClientHandler.java
package learningNote.IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class BIOClientHandler implements Runnable {
private Socket s;
public BIOClientHandler(Socket s) {
// TODO Auto-generated constructor stub
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try(BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter out=new PrintWriter(s.getOutputStream(),true)){
while(true){
//从控制台读取输入
String message=" ";
while(true){
String temp=br.readLine();
if(temp.equals("ok"))break;
message+=temp+"\n";
}
//发送消息
out.println(message+"ok");
//读取消息
String body=" ";
while(true){
String temp=in.readLine();
if(temp.equals("ok"))break;
body+=temp+"\n";
}
System.out.print(body);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}