本文代碼來源於《實戰java高併發程序設計》葛一鳴 郭超 著
學習這本書的過程中,感覺這一部分比較重要,自己做下總結,也希望能給大家提供些幫助。
本代碼模擬簡單的Echo服務器,對於Echo服務器,他會讀取客戶端的一個輸入,並將這個輸入原封不動地返回給客戶端。雖然實例很簡單,但麻雀雖小五臟俱全,需要一套完整的Socket處理機制。適合拿來學習。下面貼出本例代碼。
1、服務端代碼:
服務器會爲每一個客戶端連接啓動一個線程,這個新的線程會全心全意爲這個客戶端服務。同時,爲了接受客戶端連接,服務器還會額外使用一個派發線程,如上圖:
package socket.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
* @author FHY
* 多線程應用的服務端代碼
*
*/
public class MultiThreadEchoServer {
//創建一個線程池,不限制連接的線程數量
public static ExecutorService tp = Executors.newCachedThreadPool();
static class HandleMsg implements Runnable{
Socket clientSocket;
public HandleMsg(Socket clientSocket){
this.clientSocket = clientSocket;
}
@Override
public void run() {
BufferedReader is = null;
PrintWriter os = null;
try{
//輸入流 (使用了裝飾模式)
is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//輸出流
os = new PrintWriter(clientSocket.getOutputStream(), true);
String inputLine = null;
long b = System.currentTimeMillis();
while ((inputLine = is.readLine())!= null ){
os.println(inputLine);
}
long e = System.currentTimeMillis();
System.out.println("Spend: "+(e-b));
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(is != null) is.close();
if(os != null) os.close();
clientSocket.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ServerSocket echoSocket = null;
Socket clientSocket = null;
try{
//設置服務端的端口號
echoSocket = new ServerSocket(8000);
}catch(IOException e){
System.out.println(e);
}
while(true){
try{
//接受客戶端
clientSocket = echoSocket.accept();
System.out.println(clientSocket.getRemoteSocketAddress() + " connet!");
tp.execute(new HandleMsg(clientSocket));
}catch(IOException e){
System.out.println(e);
}
}
}
}
這就是一個支持多線程的服務端的核心內容。它的特點是,在相同可支持的線程範圍內,可以儘量多地支持客戶端的數量,同時和單線程服務器相比,它可以更好的使用多核CPU。
2、客戶端代碼:
package socket.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
*
* @author FHY
* 多線程應用的客戶端代碼
*
*/
public class ClientSocketDemo {
public static void main(String[] args) {
Socket client = null;
PrintWriter writer = null;
BufferedReader reader = null;
try{
client = new Socket();
//客戶端連接到服務端
client.connect(new InetSocketAddress("localhost", 8000), 1000*6);
writer = new PrintWriter(client.getOutputStream(), true);
writer.println("Hello!");
writer.flush();
reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("from server:" +reader.readLine());
}catch(UnknownHostException e){
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally {
try{
if(writer != null) writer.close();
if(reader != null) reader.close();
if(client != null) client.close();
}catch(IOException e){
System.out.println(e);
}
}
}
}
總結:這種多線程的服務器開發模式是及其常用的,對於絕大多數應用來說,這種模式可以很好地工作,但是如果你想讓程序工作地更加有效,就必須知道這種模式的一個重大弱點——那就是它傾向於讓CPU進行IO等待。
使用java的NIO就可以將上面的網絡IO等待時間從業務處理線程中抽取出來。