在传统的单线程服务器工作模式下,一个线程的阻塞会引起整个服务器被阻塞,最终导致服务器效率的下降,响应时间变长,影响用户体验。为了解决这个问题,我们引进多线程服务器模式。
Java的多线程可谓是Java编程的精华之一,运用得当可以极大地改善程序的响应时间,提高程序的并行性。在服务器程序中,由于往往要接收不同客户机的同时请求或命令,因此可以对每个客户机的请求生成一个命令处理线程,同时对各用户的指令作出反应。主线程负责accept()连接,而工作线程负责处理业务逻辑和流的读取等。这样,即使在工作线程阻塞的情况下,也只是阻塞在线程范围内,从而改善用户体验。实践证明,采用多线程设计可以很好的改善系统的响应,并保证用户指令执行的独立性。由于Java本身是“线程安全”的,因此有一条编程原则是能够独立在一个线程中完成的操作就应该开辟一个新的线程。
Java中实现线程的方式有两种,一是生成Thread类的子类,并定义该子类自己的run方法,线程的操作在方法run中实现。但我们定义的类一般是其他类的子类,而Java又不允许多重继承,因此,第二种实现线程的方法是实现Runnable接口。通过覆盖Runnable接口中的run方法实现该线程的功能。本文例子采用第一种方法实现线程。
多线程服务器端实现
package com.xieyincai.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; class MultiTask extends Thread { private Socket socket; public MultiTask(Socket socket) { this.socket = socket; } public void run() { try { System.out.println(Thread.currentThread().getName()); OutputStream out = socket.getOutputStream(); InputStream in = socket.getInputStream(); byte[] buffer = new byte[4096]; int length = in.read(buffer); String data = new String(buffer, 0, length, "UTF-8"); System.out.println("CLIENT:\t" + socket.getInetAddress() + " : " + socket.getPort() + "\t" + data); data = "ECHO:\t" + data; out.write(data.getBytes("UTF-8")); out.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public class MultithreadedTCPServer { private final static int PORT = 8000; public static void main(String[] args) { // TODO Auto-generated method stub ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(PORT); while(true) { Socket socket = serverSocket.accept(); Thread task = new MultiTask(socket); task.start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
TCP客户端实现
package com.xieyincai.net; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; public class SimpleTCPClient { private final static String HOSTNAME = "localhost"; private final static int PORT = 8000; public static void main(String[] args) { // TODO Auto-generated method stub Socket socket = new Socket(); try { SocketAddress address = new InetSocketAddress(HOSTNAME, PORT); socket.connect(address); OutputStream out = socket.getOutputStream(); InputStream in = socket.getInputStream(); String data = String.valueOf(System.nanoTime()); System.out.println(data); out.write(data.getBytes("UTF-8")); out.flush(); byte[] buffer = new byte[4096]; int length = in.read(buffer); String result = new String(buffer, 0, length, "UTF-8"); System.out.println(result); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
服务器端输出:
Thread-0 CLIENT: /127.0.0.1 : 51026 16117272366854 Thread-1 CLIENT: /127.0.0.1 : 51044 16121322176950 Thread-2 CLIENT: /127.0.0.1 : 51057 16124135648583 Thread-3 CLIENT: /127.0.0.1 : 51284 16174523510263
客户端输出:
16174523510263 ECHO: 16174523510263