JAVA网络编程之基于线程池的多线程服务器端的简单实现

线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用。因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等。

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程,每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

任务调度以执行线程的常见方法是使用同步队列,称作任务队列。池中的线程等待队列中的任务,并把执行完的任务放入完成队列中。Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。下面以FixedThreadPool为例说明:

服务器端实现

package com.xieyincai.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

class Task extends Thread {
	private Socket socket;
	
	public Task(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 TreadPoolTCPServer {

	private final static int PORT = 8000;
	
	private static final int THREADNUMBER = 3;
	private static final Executor exec = Executors.newFixedThreadPool(THREADNUMBER);
	
	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 Task(socket);
				exec.execute(task);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

客户端实现

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();
			}
		}
	}

}

服务器端输出:

pool-1-thread-1
CLIENT:	/127.0.0.1 : 60851	104794825541762
pool-1-thread-2
CLIENT:	/127.0.0.1 : 60887	104802533865797
pool-1-thread-3
CLIENT:	/127.0.0.1 : 60897	104803969434825
pool-1-thread-1
CLIENT:	/127.0.0.1 : 60922	104809653807016
pool-1-thread-2
CLIENT:	/127.0.0.1 : 60940	104813111964821
pool-1-thread-3
CLIENT:	/127.0.0.1 : 60948	104814391108875
pool-1-thread-1
CLIENT:	/127.0.0.1 : 61006	104828106929024

客户端输出:

104828106929024
ECHO:	104828106929024

Leave a Reply

Your email address will not be published. Required fields are marked *