本节会从软件开发功能需求的角度引出 Java线程池 的功能,然后我们会自己实现这个功能,最后再分析JUC Executors 的源码。

Doug Lea < Java Concurrcy In Practice > 第六章 任务执行 中描述:

大多数的应用程序是围绕 任务 进行组织的。任务就是抽象的、独立的工作单元。把应用程序的工作分离为一个个的任务,可以简化应用程序(分而治之的思想)。

服务器应用程序应该具有:

1、良好的吞吐量和快速的响应能力;

2、在负载越来越大时,性能会平缓的下降,而不是直接导致简单粗暴的程序崩溃

一个线程串行执行任务 和 每个任务开启一个线程都存在一些问题。

一个线程串行执行任务的问题:应用程序吞吐量和相应能力都很差。

每个任务开启一个线程执行(thread-per-task)的问题:

1、应用程序需求

功能需求:服务器应用程序会不断接收客户端的请求,收到请求后,应用程序对请求进行处理。

非功能需求:支撑高并发和高可用

2、应用程序设计

根据功能需求的描述,我们抽出几个名词和动词:

名词:服务器应用程序;客户端的请求;

动词:接收请求;对请求进行处理;

伪代码:

 class ServerApp{boolean receive(Request req);boolean process(Request req)}class Request{}

上面的实现是最直接的实现方式,但我们在写程序的时候还要考虑程序的给功能需求。

根据文章开头描述,我们不能使用上面的这种实现方式,这种是第一种串行的执行方式。

采取第二种方式,伪代码如下:

 class ServerApp{boolean receive(Request req){new Thread(new Runnalbe(){public void run(){process(req)}}).start();}boolean process(Request req){}}class Request{}

第二中方式也是存在问题的

我们采用线程池的方式 ,伪代码如下:

 class ServerApp{    Thread[] threads;ServerApp(int nThreads){threads = new Thread[nThreads];}boolean receive(Request req){req 绑定到 threads[?];threads[?]中调用process(req)}boolean process(Request req){}}class Request{}

上面的伪代码需要进一步细化:
1、需要将请求(任务)和线程进行绑定,这样才能将任务分配给线程
2、大部分的情况下,任务是比线程数多的,所以我们需要一个地方来暂存任务请求,然后空闲的线程从这个地方来取出任务进行执行

我们尝试实现一下:

 /*** 1、单独定义一个Thread,方便和Request进行绑定*/class WorkerThread extends Thread{Request req;       public void run(){req.execute()}void setRequest(Request req){this.req = req;}}class ServerApp{ Thread[] workerThreads ;/* 2、暂存任务的地方*/BlockingQueue<Request> requestQueue;ServerApp(int nThreads){requestQueue = new LinkedBlockingQueue();workerThreads = new WorkerThread [nThreads];}boolean receive(Request req){if(有空闲的WorkerThread ){空闲的WorkerThread.setRequest(req)}else{requestQueue.add(req);}}boolean process(Request req){}}class Request{void execute();}

上面的伪代码,还有需要很多细节和设计上的问题需要解决:

1、Request 是自己定义的类,其实应该是一个客户端实现的接口,只有一个execute方法,作为一个Java 开发人员我们思考一下Java提供的API里面有这样的接口么? 很明显 Runnalbe 接口便是。

2、我们的需要实现的线程池逻辑和业务有关系么?是不是可以独立出来做成通用的功能类?

3、WorkerThread 的run方法是不是应该循环从queue中取出任务执行?

上面就是我们设计的过程,到这步我们发现要考虑很多实现上的细节了,所以我们从现在开始该进行编码了,并在编码的过程中不断的进行重构。

在实现代码前我们想想线程池的命名:线程池的作用是用来执行任务,所以我们将其命名为 ThreadPoolExecutor

public class ThreadPoolExecutor {private final BlockingQueue<Runnable> taskQueue;private List threads;public ThreadPoolExecutor(int poolSize) {this.taskQueue = new LinkedBlockingQueue<Runnable>();this.threads = new ArrayList(poolSize);for(int i=0;i<poolSize;i++){WorkerThread workerThread = new WorkerThread();threads.add(workerThread);workerThread.start();}}/*** 执行任务* @param task* @return*/public boolean execute(Runnable task) {boolean isOfferd = taskQueue.offer(task);// 队列满?return false:return truereturn isOfferd;}class WorkerThread extends Thread {public void run() {while (true) {try {Runnable task = taskQueue.take();System.err.println(taskQueue.size());task.run();} catch (InterruptedException e) {e.printStackTrace();}}}}
}

Java线程池(2) - 线程池的功能需求、设计、实现相关推荐

  1. Java中四种线程池介绍

    个人资源与分享网站:http://xiaocaoshare.com/ Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而是一个执行线程的工具.真正的线 ...

  2. Java多线程设计模式(4)线程池模式

    前序: Thread-Per-Message Pattern,是一种对于每个命令或请求,都分配一个线程,由这个线程执行工作.它将"委托消息的一端"和"执行消息的一端&qu ...

  3. java多线程抽奖_java 线程池、多线程并发实战(生产者消费者模型 1 vs 10) 附案例源码...

    导读 前二天写了一篇<Java 多线程并发编程>点我直达,放国庆,在家闲着没事,继续写剩下的东西,开干! 线程池 为什么要使用线程池 例如web服务器.数据库服务器.文件服务器或邮件服务器 ...

  4. Java并发编程:线程池

    一.为什么使用线程池 使用线程的时候直接就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降 ...

  5. Java自带的线程池Executors.newFixedThreadPool

    线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理.当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程 ...

  6. 线程池 java 新建方式_Java线程池的四种创建方式

    Java通过Executors提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. newFi ...

  7. 掌握JAVA多线程的利器-线程池

    为什么80%的码农都做不了架构师?>>>    相信大多数接触过多线程的朋友都会有这样的困惑,明明使用了多线程,为何还是一团糟?用下面两幅图再合适不过了: 理想情况下的多线程VS 现 ...

  8. 【Java 并发编程】线程池机制 ( 线程池状态分析 | 线程池状态转换 | RUNNING | SHUTDOWN | STOP | TIDYING | TERMINATED )

    文章目录 一.线程池状态分析 一.线程池状态分析 线程池的状态在 ThreadPoolExecutor 源码中定义 : private final AtomicInteger ctl = new At ...

  9. 【Java 并发编程】线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )

    文章目录 一.线程池执行任务细节分析 二.线程池执行 execute 源码分析 一.线程池执行任务细节分析 线程池执行细节分析 : 核心线程数 101010 , 最大小成熟 202020 , 非核心线 ...

  10. 【Java 并发编程】线程池机制 ( 线程池阻塞队列 | 线程池拒绝策略 | 使用 ThreadPoolExecutor 自定义线程池参数 )

    文章目录 一.线程池阻塞队列 二.拒绝策略 三.使用 ThreadPoolExecutor 自定义线程池参数 一.线程池阻塞队列 线程池阻塞队列是线程池创建的第 555 个参数 : BlockingQ ...

最新文章

  1. 百度造车和RoboTaxi利好自动驾驶?不,利好茅台
  2. Android 内存泄漏问题分析 指南
  3. 仟亿达2016年净利润2184万同比跌46%
  4. Android Activity形象描述
  5. userdel account is currently in use
  6. jl1.如何设置元素的宽高包含元素的边框和内边距
  7. 去哪儿-01-EnvironmentalPre
  8. 三个变量中怎么找出中间值_scratch图形化编程基础练习-变量交换
  9. 你真的懂语音特征吗?
  10. git 历史操作日志_git的历史记录
  11. 普林斯顿微积分读本篇二:三角学
  12. 数字电路技术可能出现的简答题_数字电子技术基础(Ⅰ)-中国大学mooc-试题题目及答案...
  13. 百度关键词搜索量查询,百度,谷歌关键词查询工具
  14. js获取元素相对于父级元素的高度
  15. ES6新特性箭头函数语法、如何正确使用箭头函数
  16. 2019年安徽大学ACM/ICPC实验室新生赛题解
  17. 【夜读】做好这6件事,让人受益一生
  18. allegro 走线切换层_高速信号走线的九大规则
  19. JDK8 超详细,肝
  20. C++ scanf()函数

热门文章

  1. multipart form-data boundary 说明
  2. Spring Cloud Gateway (六) 自定义 Global Filter
  3. jdbc显示mysql的数据_JDBC链接mysql插入数据后显示问号的原因及解决办法
  4. 华为服务器怎么查看系统日志,查询系统操作日志(operationlog)
  5. Mysql在sql中截取时间类型字段的年月日和时间-DATE_FORMAT() 函数
  6. Java中Minio基础使用
  7. 玩游戏用什么轴的机械键盘好_机械键盘下的“轴”到底是什么?
  8. java.Net.UnknownHostException异常
  9. mysql防止误操作之prompt命令提示符
  10. html如何去掉有无标题点,HTML中,如何去掉某个元素下的一些特殊标签?