簡單的Tomcat實現--1.6多執行緒
阿新 • • 發佈:2020-08-04
多執行緒處理
-
現階段我們的
bootstrap.java
是單執行緒序列的,當有多個請求同時到達時,就像上一次使用多執行緒模擬同時訪問耗時任務時,伺服器也只能序列的去訪問。 -
首先建立一個執行緒池作為工具。
-
package util; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @author :xiaosong * @description:TODO * @date :2020/8/4 15:04 */ public class ThreadUtil { private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 20, 100, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(10) ); // run方法接受runnable並利用執行緒池進行處理 public static void run(Runnable r){ threadPool.execute(r); } }
-
這裡有必要詳細的說明一下ThreadPoolExetor的構造方法引數
- 第一個引數是核心執行緒數為20
- 第二個引數表示這個執行緒池最多會出現100個執行緒
- 第三個引數和第四個引數合起來表示當新增的執行緒,即大於20的部分如果空閒時間超過60s就會被回收
- 第五個引數是快取區,使用LinkedBlockQueue來儲存那些短時間內激增的請求,這些請求不會立刻讓執行緒池增加執行緒,而是超過了這個緩衝區之後,才會增加執行緒。
-
當有請求過來時,生成Runnable物件,然後將這個物件對給執行緒池處理即可。
-
import cn.hutool.core.io.FileUtil; import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.log.LogFactory; import cn.hutool.system.SystemUtil; import http.Request; import http.Response; import util.Constant; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * @author :xiaosong * @description:專案的啟動類 * @date :2020/7/28 20:41 */ public class Bootstrap { /** * 定義伺服器的埠號 */ final static int PORT = 10086; public static void main(String[] args) { try { logJvm(); // 在port埠上新建serverSocket ServerSocket serverSocket = new ServerSocket(PORT); // 外部使用一個while迴圈,當處理完一個Socket的連結請求之後,再處理下一個連結請求 while (true) { Socket socket = serverSocket.accept(); // 從socket中接收瀏覽器的請求 Runnable runnable = new Runnable() { @Override public void run() { try { // 獲取輸入流,這個輸入流表示的是收到一個瀏覽器客戶端的請求 Request request = new Request(socket); System.out.println("瀏覽器的輸入資訊: \r\n" + request.getRequestString()); Response response = new Response(); // 先將html資訊寫入到response的Writer的StringWriter中 String uri; uri = request.getUri(); if (uri == null) { return; } if ("/".equals(uri)) { String html = "Hello JerryMice"; response.getWriter().println(html); } else { // removePrefix()方法可以去掉字串指定的字首 String fileName = StrUtil.removePrefix(uri, "/"); File file = FileUtil.file(Constant.rootFolder, fileName); if (file.exists()) { //如果檔案存在,那就去試圖訪問 String fileContent = FileUtil.readUtf8String(file); // 寫入到response中 response.getWriter().println(fileContent); // 判斷是否是模擬的耗時任務 if ("timeConsume.html".equals(fileName)) { ThreadUtil.sleep(1000); } } else { System.out.println("File not found!"); } } System.out.println(uri); // 開啟輸出流,準備給客戶端輸出資訊 handle200(socket, response); } catch (IOException e) { LogFactory.get().error(e); } } }; util.ThreadUtil.run(runnable); } } catch (IOException e) { LogFactory.get().error(e); } } private static void logJvm() { // 建立一個Map用於儲存各種資訊 Map<String, String> infoMap = new LinkedHashMap<>(); infoMap.put("Server version", "JerryMice 1.0.0"); infoMap.put("Server build", "2020-08-03"); infoMap.put("OS:\t", SystemUtil.get("os.name")); infoMap.put("OS version", SystemUtil.get("os.version")); infoMap.put("Architecture", SystemUtil.get("os.arch")); infoMap.put("Java Home", SystemUtil.get("java.home")); infoMap.put("JSM Version", SystemUtil.get("java.runtime.version")); infoMap.put("JVM Vendor", SystemUtil.get("java.vm.specification.vendor")); Set<String> keys = infoMap.keySet(); for (String key : keys) { // 呼叫hutool的LogFactory工廠函式獲取logger,logger會自動根據log4j.properties來對Log4j的Logger進行配置 LogFactory.get().info(key + ":\t\t" + infoMap.get(key)); } } /** * @param socket: * @param response:Response物件,伺服器對瀏覽器請求的響應,可以通過response的getBody()獲取儲存在其中的html文字 * @throws IOException */ private static void handle200(Socket socket, Response response) throws IOException { // 獲取型別 String contentType = response.getContentType(); String headText = Constant.responseHead200; headText = StrUtil.format(headText, contentType); byte[] head = headText.getBytes(); // 獲取response中的html文字,這個html文字是通過writer寫到stringWriter字元流上的 byte[] body = response.getBody(); byte[] responseBytes = new byte[head.length + body.length]; ArrayUtil.copy(head, 0, responseBytes, 0, head.length); ArrayUtil.copy(body, 0, responseBytes, head.length, body.length); OutputStream outputStream = socket.getOutputStream(); outputStream.write(responseBytes); socket.close(); } }
-
單元測試
-
3個耗時任務請求在1s31ms內就被全部響應完畢了,提現了多執行緒並行。