线程池技术及其实例
线程池技术的好处是,一方面,消除了频繁创建和消亡线程的系统资源开销,另一方面,面对过量任务的提交能够平缓的劣化。示例如代码清单所示。
先看一个简单的线程池接口定义。
1 public interface ThreadPool<Job extends Runnable> { 2 3 /** 4 * 执行一个Job,这个Job需要实现Runnable 5 * @param job 6 */ 7 void execute(Job job); 8 9 /** 10 * 关闭线程池 11 */ 12 void shutdown(); 13 14 /** 15 * 增加工作者线程 16 * @param num 17 */ 18 void addWorkers(int num); 19 20 /** 21 * 减少工作者线程 22 * @param num 23 */ 24 void removeWorker(int num); 25 26 /** 27 * 得到正在等待执行的任务数量 28 * @return 29 */ 30 int getJobSize(); 31 }
客户端可以通过execute(Job)方法将Job提交给线程池执行,而客户端自身不用等待Job的执行完成。除了execute(Job)方法以外,线程池接口提供了增大/减少工作者线程以及关闭线程池的方法。这里工作者线程代表着一个重复执行Job的线程,而每个有客户端提交的Job都将进入到一个工作队列中等待工作者线程的处理。代码清单所示。
import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; /** * 线程池的本质就是使用了一个线程安全的工作队列连接工作者线程和客户端线程,客户端线程将任务放入工作队列后便返回, * 而工作者线程则不断地从工作队列上取出工作并执行。当工作队列为空时,所有的工作者线程均等待在工作队列上, * 当有客户端提交了一个任务之后会通知任意一个工作者线程,随着大量的任务被提交,更多的工作者线程会被唤醒。 */ public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> { // 线程的最大限制数 private static final int MAX_WORKER_NUMBERS = 10; // 线程池默认的数量 private static final int DEFAULT_WORKER_NUMBERS = 5; // 线程池最小的数量 private static final int MIN_WORKER_NUMBERS = 1; // 这是一个工作列表,将会向里面插入工作 private final LinkedList<Job> jobs = new LinkedList<>(); // 工作者列表 private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>()); // 工作者线程的数量 private int workerNum = DEFAULT_WORKER_NUMBERS; // 线程编号生成 private AtomicLong threadNum = new AtomicLong(); public DefaultThreadPool() { initializeWorkders(DEFAULT_WORKER_NUMBERS); } public DefaultThreadPool(int workerNum) { this.workerNum = workerNum > MAX_WORKER_NUMBERS && workerNum < MIN_WORKER_NUMBERS ? MIN_WORKER_NUMBERS : workerNum; initializeWorkders(this.workerNum); } @Override public void execute(Job job) { if (job != null) { // 添加一个工作,然后进行通知 synchronized (jobs) { jobs.addLast(job); jobs.notify(); } } } @Override public void shutdown() { for (Worker worker : workers) { worker.shutdown(); } } @Override public void addWorkers(int num) { synchronized (jobs) { // 限制新增的Worker数量不能超过最大值 if (num + this.workerNum > MAX_WORKER_NUMBERS) { num = MAX_WORKER_NUMBERS - this.workerNum; } initializeWorkders(num); this.workerNum += num; } } @Override public void removeWorker(int num) { synchronized (jobs) { if (num >= this.workerNum) { throw new IllegalArgumentException("beyond workNum"); } // 按照给定的数量停止Worker int count = 0; while (count < num) { Worker worker = workers.get(count); if (workers.remove(worker)) { worker.shutdown(); count ++; } } this.workerNum -= count; } } @Override public int getJobSize() { return jobs.size(); } /** * 初始化线程工作者 * @param num */ private void initializeWorkders(int num) { for (int i = 0; i < num; i++) { Worker worker = new Worker(); workers.add(worker); Thread thread = new Thread(worker, "ThreadPool-Worker-" + threadNum.incrementAndGet()); thread.start(); } } /** * 工作者,负责消费任务 */ class Worker implements Runnable { // 是否工作 private volatile boolean running = true; @Override public void run() { while (running) { Job job = null; synchronized (jobs) { // 如果工作者列表是空的,那么就wait while (jobs.isEmpty()) { try { jobs.wait(); } catch (InterruptedException e) { // 感知到外部对WorkerThread的中断操作,返回 Thread.currentThread().interrupt(); return; } } // 取出一个Job job = jobs.removeFirst(); } if (job != null) { job.run(); } } } public void shutdown() { running = false; } } }
下面通过使用线程池来构造一个简单的Web服务器,这个web服务器用来处理http请求,目前只能处理简单的文本和JPG图片内容。
1 import java.io.*; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 5 /** 6 * 这个Web服务器用来处理HTTP请求,目前只能处理简单的文本和JPG图片内容。这个Web服务器使用main线程不断地接受客户端 7 * Socket的连接,将连接以及请求提交给线程池处理,这样使得Web服务器能够同时处理多个客户端请求。 8 */ 9 public class SimpleHttpServer { 10 11 // 处理HttpRequest的线程池 12 private static ThreadPool<HttpRequestHandler> threadPool = new DefaultThreadPool<HttpRequestHandler>(); 13 14 // SimpleHttpServer的跟路径 15 private static String basePath; 16 private static ServerSocket serverSocket; 17 private static int port = 8080; 18 19 public static void setBasePath(String basePath) { 20 if (basePath != null && new File(basePath).exists() && new File(basePath).isDirectory()) { 21 SimpleHttpServer.basePath = basePath; 22 } 23 } 24 25 public static void start() throws IOException { 26 serverSocket = new ServerSocket(port); 27 Socket socket = null; 28 while ((socket = serverSocket.accept()) != null) { 29 // 接受一个客户端Socket,生成一个HttpRequestHandler,放入线程池执行 30 threadPool.execute(new HttpRequestHandler(socket)); 31 } 32 serverSocket.close(); 33 } 34 35 public static void setPort(int port) { 36 if (port > 0) { 37 SimpleHttpServer.port = port; 38 } 39 } 40 41 private static class HttpRequestHandler implements Runnable { 42 43 private Socket socket; 44 45 public HttpRequestHandler(Socket socket) { 46 this.socket = socket; 47 } 48 49 @Override 50 public void run() { 51 String line = null; 52 BufferedReader bufferedReader = null; 53 BufferedReader reader = null; 54 PrintWriter out = null; 55 InputStream in = null; 56 57 try { 58 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 59 String header = reader.readLine(); 60 // 由相对路径计算出绝对路径 61 String filePath = basePath + header.split(" ")[1]; 62 out = new PrintWriter(socket.getOutputStream()); 63 // 如果请求资源的后缀为jpg或者ico,则读取资源并输出 64 if (filePath.endsWith("jpg") || filePath.endsWith("ico")) { 65 in = new FileInputStream(filePath); 66 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 67 int i = 0; 68 while ((i = in.read()) != -1) { 69 baos.write(i); 70 } 71 byte[] array = baos.toByteArray(); 72 out.println("HTTP/1.1 200 OK"); 73 out.println("Server: Molly"); 74 out.println("Content-Type: image/jpeg"); 75 out.println("Content-Length: " + array.length); 76 out.println(""); 77 socket.getOutputStream().write(array, 0, array.length); 78 } else { 79 bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(filePath))); 80 out = new PrintWriter(socket.getOutputStream()); 81 out.println("HTTP/1.1 200 OK"); 82 out.println("Server: Molly"); 83 out.println("Content-Type: text/html; charset=UTF-8"); 84 out.println(""); 85 while ((line = bufferedReader.readLine()) != null) { 86 out.println(line); 87 } 88 } 89 out.flush(); 90 } catch (IOException e) { 91 out.println("HTTP/1.1 500"); 92 out.println(""); 93 out.flush(); 94 } finally { 95 close(bufferedReader, in, reader, out, socket); 96 } 97 98 } 99 100 /** 101 * 关闭流或者Socket 102 * @param closeables 103 */ 104 private void close(Closeable... closeables) { 105 if (closeables != null) { 106 for (Closeable closeable : closeables) { 107 try { 108 closeable.close(); 109 } catch (IOException e) { 110 e.printStackTrace(); 111 } 112 } 113 } 114 } 115 } 116 }