线程池(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