前言:首先简单模拟一个场景,前端有一个输入框,有一个按钮,点击这个按钮可以实现搜索输入框中的相关的文本和图片(类似于百度、谷歌搜索).看似一个简单的功能,后端处理也不难,前端发起一个请求,后端接受到这个请求,获取前端输入的内容,然后用搜索服务查找相关的数据返回给前端。但是问题来了,可能不是一个用户在搜索,假如有一万个用户同时发起请求呢?后端如何处理?如果按照单机的 处理方式,很容易线程堵死,程序崩溃、数据库崩塌。本文来介绍一下如何通过线程池来处理前端的请求。

本篇博客的目录

一:线程池的优点

二:定义一个线程池

三:  线程池实现类

四:执行任务

五:总结

本篇博客技术总架构图:

一:线程池的好处

  1.1:好处

1.1.1  线程池可以异步的执行任务,当任务进来的时候线程池首先会判断当前是否有存活可用的线程,如果有的话,线程会执行这个任务。但是任务此时可以立刻返回,并不一定必须等待任务执行完毕才会返回。假如是同步阻塞的话,当一个线程遇到Exception的时候,假如这个线程没有得到处理,那么就会造成线程堵塞,资源囤积,最终的结果只能是cpu资源耗尽,所有的任务无法处理。之前我们的线上就出现了很多dubbo服务访问超时问题,最后发现就是cpu资源耗尽,报了一个unable to create new Thread,这样就无法处理任务(最后我们进行了物理扩容并且合理限定了线程池的最大线程数量才解决这个问题)

1.1.2:线程池可以集中管理线程,可以控制线程的运行周期,这里包括动态添加线程或者移除线程。有一个很重要的点是这样的:线程的上下文切换是非常消耗性能的;假如来了一个任务,线程执行一次,然后立刻销毁;再来一个任务,再创建一个任务,用完再销毁这个线程。那么为什么不能对这个线程进行复用呢?

1.1.3:线程池的优势只有在高请求量才会体现出来,如果请求量比较好,需要处理的任务很少,那么使用线程池的作用并不明显。但是并不是线程数量越多越好,具体的数量需要评估每个任务的处理时间以及当前计算机的处理能力和数量,这个是有具体的数据体现的,我们来看一下实际数据比较:

二:定义一个线程池

 2.1:首先我们来定义一个线程池的接口,其中包含线程池开始任务、关闭线程池,增加线程、减少线程,线程池的使命就是管理线程的生命周期,包括加、减少和删除线程,还有让线程开始执行任务,自身的关闭和开启!

public interface ThreadPool<Job extends Runnable> {/*** 线程池开始*/void execute(Job job);/*** 关闭线程池*/void shutDown();/*** 添加线程* @param num*/void addWorkers(int num);/*** 减少线程* @param num*/void removeWorker(int num);/*** 获取正在等待的线程数量* @return*/int getJobSize();
}

三:线程池实现类

解释: 线程池的实现类,首先就是定义线程池的默认数量,为2*cpu核心数+1,这是比较合理的计算公式。最小数量定义为1,最大数量定义为10。还用一个LinkedList来作为工作线程的集合容器。这里为什么要用linkedList而不是ArrayList呢?因为linkedList是一个双向链表,双向链表可以实现先进先出或者后进先出等集合。然后我们定义了worker来封装具体执行任务的线程,用Job来封装要执行的任务。然后在构造方法里用initWorkers方法来初始化线程池,创建指定的默认数量的线程,指定名称(用AtomicLong:原子线程安全的)并添加到管理线程的集合workers中(这个list经过synchronizedList修饰它已经成为了一个同步的集合,所做的操作都是线程安全的)。在execute中,首先获取需要指定的任务(Job),为了保证线程安全,会锁住所有的任务集合(放心synchronized这个关键字的作用,它经过jdk1.7已经优化过了,性能消耗有质的提升)。这里为什么要锁住jobs这个集合呢,答案是:为了防止在多线程环境下,有多个job同时添到这个jobs里面,任务要一个个的执行,防止无法执行任务。接着再用addLast方法将任务添加到链表的最后一个,这里就是一个先进先出的队列(先进入的线程会优先被执行)再调用jobs的notify方法唤醒其他job。而在下面的添加线程或者移除线程的方法,都必须要锁住整个工作队列,这里为了防止,执行的时候突然发现job不见了,或者添加的时候取不到最新的job等多线程下的安全问题,并且在worker线程中增加了一个running字段,用于控制线程的运行或者停止(run方法是否执行的控制条件)

public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {private static final int MAX_WORKER_NUMBERS = 10;private static final int DEFAULT_WORKERS_NUMBERS = 2 * (Runtime.getRuntime().availableProcessors()) + 1;private static final int MIN_WORDER_NUMBERS = 1;private final LinkedList<Job> jobs = new LinkedList<Job>();//管理工作线程的集合private final List<Worker> workers = Collections.synchronizedList(new ArrayList<Worker>());private int workerNum = DEFAULT_WORKERS_NUMBERS;private AtomicLong threadNum = new AtomicLong();/*** 线程开始运行*/public DefaultThreadPool() {initWorkers(DEFAULT_WORKERS_NUMBERS);}public DefaultThreadPool(int num) {workerNum = num > MAX_WORKER_NUMBERS ? MAX_WORKER_NUMBERS : num < MIN_WORDER_NUMBERS ? MIN_WORDER_NUMBERS : num;}/*** 初始化线程** @param defaultWorkersNumbers*/private void initWorkers(int defaultWorkersNumbers) {for (int i = 0; i < defaultWorkersNumbers; i++) {Worker worker = new Worker();workers.add(worker);Thread thread = new Thread(worker, "ThreadPool-worker" + threadNum.incrementAndGet());thread.start();}}/*** 执行任务** @param job*/@Overridepublic void execute(Job job) {if (job != null) {synchronized (jobs) {jobs.addLast(job);jobs.notify();}}}/*** 关闭线程*/@Overridepublic void shutDown() {for (Worker worker : workers) {if (worker != null) {worker.shutDown();}}}/*** 添加线程** @param num*/@Overridepublic void addWorkers(int num) {synchronized (jobs) {if (num + this.workerNum > MAX_WORKER_NUMBERS) {num = MAX_WORKER_NUMBERS - this.workerNum;}initWorkers(num);this.workerNum += num;}}/*** 移除线程** @param num*/@Overridepublic void removeWorker(int num) {synchronized (jobs) {if (num > workerNum) {throw new IllegalArgumentException("much workNum");}int count = 0;while (count < num) {Worker worker = workers.get(count);if (workers.remove(worker)) {worker.shutDown();count++;}}this.workerNum -= count;}}/*** 获取工作线程的数量** @return*/@Overridepublic int getJobSize() {return jobs.size();}/*** 工作线程*/public class Worker implements Runnable {private volatile boolean running = false;@Overridepublic void run() {while (running) {Job job = null;synchronized (jobs) {while (jobs.isEmpty()) {try {jobs.wait();} catch (InterruptedException ex) {ex.printStackTrace();Thread.currentThread().interrupt();return;}}job = jobs.removeFirst();}if (job != null) {try {job.run();} catch (Exception ex) {ex.printStackTrace();}}}}public void shutDown() {this.running = false;}}
}

四:简易的web http处理线程池

4.1:定义一个类,叫做SimlpeHttpHandler,其中维护着一个叫做HttpRequestHandler的job,这个job的作用就是通过socket监听固定的端口(8080),然后通过流读取web目录中的文件,根据不同的文件格式封装打印返回

public class SimleHttpHandler {static ThreadPool<HttpRequestHandler> threadPool = new DefaultThreadPool<HttpRequestHandler>(1);public String basePath;private ServerSocket serverSocket;@Resourceprivate HttpRequestHandler httpRequstHandler;int port = 8080;/*** 设置端口** @param port*/public void setPort(int port) {if (port > 0) {this.port = port;}}/*** 设置基本路径** @param basePath*/public void setBasePath(String basePath) {if (basePath != null) {boolean exist = new File(basePath).exists();boolean directory = new File(basePath).isDirectory();if (exist && directory) {this.basePath = basePath;}}}/*** 开始线程** @throws Exception*/public void start() throws Exception {serverSocket = new ServerSocket(port);Socket socket = null;while ((socket = serverSocket.accept()) != null) {try {threadPool.execute(new HttpRequestHandler(socket, basePath));} catch (Exception ex) {ex.printStackTrace();} finally {serverSocket.close();}}}
}

4.2:定义一个类叫做HttpRequestHandler实现Runnable接口,然后构造进入socket和路径,在run方法中调用具体的处理方法:我将具体的业务封装到

ServerRequestManager中,然后调用它的dealRequest方法进行具体的业务处理:
@Component
public class HttpRequestHandler implements Runnable {private Socket socket;private String basePath;@Resourceprivate ServerRequestManager serverRequestManager;public HttpRequestHandler(Socket socket, String basePath) {this.basePath = basePath;this.socket = socket;}@Overridepublic void run() {serverRequestManager.dealRequest(basePath);}
}

4.3:服务器的具体处理逻辑,这里就是根据当前的路径用流读取路径中的文件,一旦检测到文件的后缀是.jpg或者ico,就将其输出为http的内容类型为img类型的图片,否则输出为text类型。最后用colse方法来关闭流

 */
@Component
public class ServerRequestManager {private Socket socket;public static final String httpOK = "HTTP/1.1 200 ok";public static final String molly = "Server:Molly";public static final String contentType = "Content-Type:";/*** 处理请求** @param basePath*/public void dealRequest(String basePath) {String content = null;BufferedReader br = null;BufferedReader reader = null;PrintWriter out = null;InputStream in = null;try {reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));String header = reader.readLine();String filePath = basePath + header.split(" ")[1];out = new PrintWriter(socket.getOutputStream());if (filePath.endsWith("jpg") || filePath.endsWith("ico")) {in = new FileInputStream(filePath);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();int index = 0;while ((index = in.read()) != -1) {byteArrayOutputStream.write(index);}byte[] array = byteArrayOutputStream.toByteArray();out.print(httpOK);out.print(molly);out.println(contentType + " image/jpeg");out.println("Content-Length" + array.length);out.print("");socket.getOutputStream().write(array, 0, array.length);} else {br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));out = new PrintWriter(socket.getOutputStream());out.print(httpOK);out.print(molly);out.print(contentType + "text/html; Charset =UTF-8");out.print("");while ((content = br.readLine()) != null) {out.print(content);}}out.flush();} catch (final Exception ex) {ex.printStackTrace();out.println("HTTP/1.1 500");out.println("");out.flush();} finally {close(br, in, out, socket);}}/*** 关闭流** @param closeables*/public static void close(Closeable... closeables) {if (closeables != null) {for (Closeable closeable : closeables) {try {closeable.close();} catch (Exception ex) {ex.printStackTrace();}}}}
}

五:总结

本篇博客总结了如何开发一个简单的线程池,当然功能不够齐全,比不上jdk的线程池,没有阻塞队列和超时时间和拒绝策略等;然后会用socket监听8080端口,获取web根目录读取目录下的文件,然后输出对应的格式内容。实现的功能很简单,没有什么复杂的,不过我觉的这篇这篇博客能让我学习的地方就是线程池的使用方法,在处理高并发的请求时,线程池技术基本是必不可少的。

参考资料《java并发编程的艺术》

*假如你想学习java,或者看本篇博客有任务问题,可以添加java群:618626589

转载于:https://www.cnblogs.com/wyq178/p/10507777.html

基于线程池技术的web服务器相关推荐

  1. dhcp计算机毕业论文,基于线程池机制的高性能DHCP服务器研究与实现-计算机科学与技术专业毕业论文.docx...

    文档介绍: 西北丁业大学硕士学位论文 摘要摘 要随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径.目前,多数DHCP服务器是单线程 ...

  2. 基于Linux的C++轻量级web服务器/webserver/httpserver——线程池

    1. 背景 什么是线程池? 线程池技术是池化技术的一种.除了线程池,还是内存池.连接池等其他池化技术.打个比方来说,线程池是将若干个随时可以执行任务的线程放在"池子"这种容器中,当 ...

  3. 高性能dhcp服务器,基于线程池机制的高性能DHCP服务器研究与实现

    摘要: 随着互联网的蓬勃发展,IP地址资源越来越紧张.DHCP服务是在现有IPv4协议基础上解决IP地址资源短缺问题的有效途径. 目前,多数DHCP服务器是单线程运行,串行处理客户请求的.其应用于大型 ...

  4. 基于SmartThreadPool线程池技术实现多任务批量处理

    一.多线程技术应用场景介绍 本期同样带给大家分享的是阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何采用基于开源组件SmartThreadPool线程池技术实现多任务批量处理.在工作中您是否 ...

  5. Hystrix面试 - 基于 Hystrix 线程池技术实现资源隔离

    Hystrix面试 - 基于 Hystrix 线程池技术实现资源隔离 上一讲提到,如果从 Nginx 开始,缓存都失效了,Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做 ...

  6. 项目--基于http协议的小型web服务器

    在我们对网络的学习过程中,会接触到网络编程,我们在网络中可以深刻认识到服务器与客户端的交互,当我们输入网址时背后发生的一系列后端操作,为了加深我们对网络部分的学习,我们找到了一个开源项目TinyWeb ...

  7. C# SmartThreadPool线程池技术实现多任务批量处理

    一.多线程技术应用场景介绍 本期同样带给大家分享的是阿笨在实际工作中遇到的真实业务场景,请跟随阿笨的视角去如何采用基于开源组件SmartThreadPool线程池技术实现多任务批量处理.在工作中您是否 ...

  8. 基于epoll实现简单的web服务器

    1. 简介 epoll 是 Linux 平台下特有的一种 I/O 复用模型实现,于 2002 年在 Linux kernel 2.5.44 中被引入.在 epoll 之前,Unix/Linux 平台下 ...

  9. arm Linux 低成本方案,参赛作品《低成本基于ARM+Linux平台搭建web服务器的物联网学习板》...

    [报名阶段需要填写的内容] 1. 参赛者姓名(必填项): 王徕泽 2. 单位或学校名称(选填项): 徕泽电子工作室 3. 当前职务或职称(选填项): 室长 4. 参赛作品的名字(必填项): 低成本基于 ...

  10. 20170916_Linux下线程池技术

    20170916_Linux下线程池技术 1.问题的起因: (1)在服务器程序中,经常是每当来一个客户端的连接请求时,服务器主线程就会创建一个与之对应的子线程来为这个客户端进行服务,也就是进行数据的I ...

最新文章

  1. python自动华 (十四)
  2. 如何在Github上精准地找到想要的开源项目?
  3. 干货丨机器学习?人工智能?还在傻傻分不清楚?
  4. 如何:对 SharePoint 列表项隐藏 ECB 中的菜单项
  5. HTML(二):表格元素
  6. OpenGL相机控制之二
  7. java int数组写入文件中_Java程序将int数组写入文件
  8. Linux主流架构运维工作简单剖析
  9. 红外线摄像机的选择与使用及原理
  10. Solaris是出色的Java开发平台的原因
  11. redis 内存不足 排查_排查redis占用内存达90%以上
  12. 无符号哥伦布指数编码
  13. 项目的启动顺序_多个项目进行如何做好进度管理
  14. 变量定义类型长度的理解
  15. HTML5 学习笔记(二)——HTML5新增属性与表单元素
  16. 面向对象(Python):学习笔记之多态
  17. 27岁,大专学历,女程序员内心的感受和行业焦虑
  18. 敏捷软件开发(2)--- 设计原则
  19. unity上传头像_Unity用户自定义圆角头像
  20. Layui框架基本使用

热门文章

  1. 关于python编程语法_Python编程入门——基础语法详解
  2. 计算机音乐tfboys手机,TFBoys王源的iPhone挂了 别怕 手贱有得治
  3. python django部署_Python+django部署(一)
  4. 计算机个性化设计小组工作计划,电脑兴趣小组工作计划.doc
  5. python 智能造句_用python中的markov链造句
  6. nohup命令的用法
  7. openstack连通性检查显示验证失败_从超大规模部署到一体机,浪潮云海引领OpenStack落地新范式...
  8. 【BZOJ3916】friends(hash+分情况讨论)
  9. 华为hs8545m如何复位_在华为东莞松山湖基地,见证一场始于AI质检的智能制造变革...
  10. 程序员Java代码不会写,菜鸡程序员都是怎样写代码的?