• 用途及用法

网络请求通常有两种形式:第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等。另 一种形式是请求频繁,但是连接上以后读/写很少量的数据就断开连接。考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的 资源可能会造成很大的浪费,特别是第二种情况。因为通常情况下,创建线程是需要一定的耗时的,设这个时间为T1,而连接后读/写服务的时间为T2,当 T1>>T2时,我们就应当考虑一种策略或者机制来控制,使得服务对于第二种请求方式也能在较低的功耗下完成。

通常,我们可以用线程池来解决这个问题,首先,在服务启动的时候,我们可以启动好几个线程,并用一个容器(如线程池)来管理这些线程。当请求到来 时,可以从池中去一个线程出来,执行任务(通常是对请求的响应),当任务结束后,再将这个线程放入池中备用;如果请求到来而池中没有空闲的线程,该请求需 要排队等候。最后,当服务关闭时销毁该池即可。

  • 结构

线程池中通常由这样几个概念(接口)组成:

  1. 线程池(Thread pool ),池是一个容器,容器中有很多个执行器,每一个执行器是一个线程。当然,这个容器的实现,可以是链表,可以是数组等等,不需要关心,需要关心的是,池必须提供一个可以从中取出执行器 的方法,可能还需要一个池中现有活动线程数方法,销毁池的方法等。
  2. 执行器(Executor ),每个执行器是一个线程,每个执行器可以执行一个任务 ,任务是做什么,此时还不很明确,它需要提供任务的setter/getter方法,并且作为一个线程,他可以独立运行,执行器执行完自身后,需要将自身放入池中。
  3. 任务(Task ),任务是每个线程具体要做的事,如资源下载,播放flash片段,打印一段文字到控制台等等,它本身不能执行,而需要将自身交给执行器。

整个池的机制和结构就是这样,当然,需要一个调度者(scheduler)来协调主线程和池的关系。结构,或者接口的目的是为了让我们从细节中解脱 出来,从一个比较抽象的层次来描述系统,这样的好处是简单,而且设计出来的框架比较通用,可以适应很多相近相似的情况。由于Task具体干什么我们不知 道,所以它几乎可以干任何适应于上边总结的网络连接的第二种情况(T1>>T2)。

  • 类的结构图

虽然为一个简单的实现设计一个标准的UML视图是不太现实的,但是这是一种受鼓励的做法,至少应该用铅笔在草纸上画出相关的视图,这样可以帮助以后的维护和更高级的扩展。

  • 线程池的简单实现

实现可以是通过多种语言的,我们在此选择面向对象的JAVA,而如果你使用C的话,也没有问题,问题在上一小节已经描述清楚,语言是不重要的。

池是一个容器,我们考虑使用java.util.LinkedList类(可能由于它的长度是可变的,而且不需要我们使用者来考虑),也就是说,池需要维护一个链表。

  1. public   interface  Pool { //池接口
  2. Executor getExecutor();
  3. void  destroy();
  4. }
  1. public   interface  Executor { //执行器接口
  2. void  setTask(Task task);
  3. Task getTask();
  4. void  startTask();
  5. }

鉴于执行器是池中的对象,而且外部没有必要知道其细节,我们考虑将Executor接口的实现做为Pool接口的实现的内部类。这样做的另一个好处是,更便于池的管理。

  1. import  java.util.LinkedList;
  2. import  java.util.Properties;
  3. import  redesigned.utils.PropReader;
  4. public   class  ThreadPool  implements  Pool{
  5. private   boolean  isShut;
  6. private  LinkedList pool;
  7. private   static  Properties prop = PropReader.getProperties( "webconfig.properties" );
  8. private   int  size = Integer.parseInt(prop.getProperty( "threadsperpage" ,  "3" ));
  9. public  ThreadPool(){
  10. // read configuration and set the
  11. // content of pool by objects of Executor
  12. isShut =  false ; //set the status of pool to active
  13. pool =  new  LinkedList();
  14. for ( int  i =  0 ; i < size; i++){
  15. Executor executor =  new  ExecutorImpl(); //new a executor thread
  16. pool.add(executor); //add it to pool
  17. ((ExecutorImpl)executor).start(); //start it
  18. }
  19. }
  20. public   void  destroy() { //Destroy
  21. synchronized (pool){
  22. isShut =  true ; //set the status of pool to inactive
  23. pool.notifyAll(); //notify all listener.
  24. pool.clear(); //clear the list of threads
  25. }
  26. }
  27. public  Executor getExecutor(){
  28. Executor ret =  null ;
  29. synchronized (pool){ //return if any.
  30. if (pool.size() >  0 ){
  31. ret = (Executor)pool.removeFirst();
  32. } else {
  33. try  {
  34. pool.wait();
  35. }  catch  (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. ret = (Executor)pool.removeFirst();
  39. }
  40. }
  41. return  ret;
  42. }
  43. Executor接口的实现作为ThreadPool的内部类
  44. private   class  ExecutorImpl  extends  Thread  implements  Executor{
  45. private  Task task;
  46. private  Object lock =  new  Object();
  47. //private boolean loop = true;
  48. public  ExecutorImpl(){}
  49. public  Task getTask() {
  50. return   this .task;
  51. }
  52. public   void  setTask(Task task) {
  53. this .task = task;
  54. }
  55. public   void  startTask(){
  56. //System.out.println("start here");
  57. synchronized (lock){
  58. lock.notify();
  59. }
  60. }
  61. public   void  run(){
  62. //get a task if any
  63. //then run it
  64. //then put self to pool
  65. while (!isShut){
  66. synchronized (lock){
  67. try  {
  68. lock.wait(); //wait for resource
  69. }  catch  (InterruptedException e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. getTask().execute(); //execute the task
  74. synchronized (pool){ //put it self to the pool when finish the task
  75. pool.addFirst(ExecutorImpl. this );
  76. pool.notifyAll();
  77. }
  78. }
  79. }
  80. }
  81. }

好了,池设计好了,再来看看任务(Task)的接口和实现

  1. public   interface  Task { //这个接口也比较简单,可以执行,可以取到执行结果
  2. void  execute();
  3. byte [] getResult();
  4. }

Task的实现可以是多种多样的,下边的例子是一个加载资源的Task.使用方式

Java代码

  1. Pool pool =  new  ThreadPool(); // new a ThreadPool
  2. //load resources on each page, and start #s of thread.
  3. for ( int  i =  0 ; i < resourceList.size();i++){
  4. Executor executor = pool.getExecutor();  // get Executor form pool
  5. Task resourceLoader =  new  ResourceLoader((String)resourceList.get(i));
  6. executor.setTask(resourceLoader);  // set the task to executor
  7. executor.startTask();  // try to start the executor.
  8. }
  9. //wait while all task are done, the destroy the pool.
  10. pool.destroy();
  • 优势,或者适用范围
  1. 在并发时,需要被并发的线程不需要知道自己什么时候需要被启动,它子需要考虑这样一种情况:它自己从一个地方取出来一个执行器,然后把任务交给执 行器,然后等待执行器结束即可,他关心的是自己所需要干的事,或者自己负责的事。这样,大家都简单,因为只需要做好自己的事情就好了。面向对象的一个秘诀 为:永远相信合作者,使用别人的接口而不是自己去实现所有的接口。
  2. 这种T1>>T2的请求方式在网络中固然是常见的,在实际问题中同样是常见的。因此,掌握这种模式可能会对我们以后的程序设计提供方便和好处。
  • 小结

同步问题: 同步在线程的并发中意义非常之大,对临界资源的控制是并发时最关键的地方。如在线程池中,当池中没有空闲的线程时,新来的请求就必须等待,而一旦一个 Task运行结束后,一方面将自己放入池中,一方面需要通知等待在pool中的其他线程。每一个执行器线程,一开始启动,则进入等待状态,此时不会消耗 CPU资源。而当在外部调用执行器的startTask()方法,即可通知线程从等待状态中醒来,去出Task,执行之,将执行器本身放入池中,然后继续 等待。

当然,实现的策略是可以多种多样的,但是问题的本质已经在第二小节结构 很明确的被定义了。

最近开始学习JAVA,同时也开始熟悉面向对象的思维方式,这篇日志,一来作为一个备忘,二来可以对可能需要的人提供帮助。以上可以算作是小结。

JAVA线程池例子的更多相关文章

  1. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  2. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  3. Java线程池的几种实现 及 常见问题讲解

    工作中,经常会涉及到线程.比如有些任务,经常会交与线程去异步执行.抑或服务端程序为每个请求单独建立一个线程处理任务.线程之外的,比如我们用的数据库连接.这些创建销毁或者打开关闭的操作,非常影响系统性能 ...

  4. Java线程池的原理及几类线程池的介绍

    刚刚研究了一下线程池,如果有不足之处,请大家不吝赐教,大家共同学习.共同交流. 在什么情况下使用线程池? 单个任务处理的时间比较短 将需处理的任务的数量大 使用线程池的好处: 减少在创建和销毁线程上所 ...

  5. [转 ]-- Java线程池使用说明

    Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...

  6. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

  7. JAVA线程池应用的DEMO

    在做很多高并发应用的时候,单线程的瓶颈已经满足不了我们的需求,此时使用多线程来提高处理速度已经是比较常规的方案了.在使用多线程的时候,我们可以使用线程池来管理我们的线程,至于使用线程池的优点就不多说了 ...

  8. Java线程池带图详解

    线程池作为Java中一个重要的知识点,看了很多文章,在此以Java自带的线程池为例,记录分析一下.本文参考了Java并发编程:线程池的使用.Java线程池---addWorker方法解析.线程池.Th ...

  9. 由浅入深理解Java线程池及线程池的如何使用

    前言 多线程的异步执行方式,虽然能够最大限度发挥多核计算机的计算能力,但是如果不加控制,反而会对系统造成负担.线程本身也要占用内存空间,大量的线程会占用内存资源并且可能会导致Out of Memory ...

随机推荐

  1. Python 调用outlook发送邮件(转 )

    单账号: import win32com.client as win32 def send_mail(): outlook = win32.Dispatch('Outlook.Application' ...

  2. windows7安装docker异常:looks like something went wrong in step ‘looking for vboxmanage.exe’

    一.背景 最近准备抽点时间研究下docker,选择在家中的windows系统上安装. 我的系统是windows7,首先安装Docker Toolbox,Docker Toolbox是一个工具集,主要包 ...

  3. Milo-OPC UA处理Subscription和Triggering

    Subscription有两种模式,一种是Reporting,另一种是Sampling. 如果定义为Sampling,则这个Subscription是一个Triggered Item,即被激发的订阅, ...

  4. [MSCOCO] Ubuntu16.04下使用 tylin/coco-caption 评价 MSCOCO Caption(配置,及Demo运行)

    Github链接:https://github.com/tylin/coco-caption Ubuntu版本信息 Linux内核版本号:Linux version 4.15.0-51-generic ...

  5. ETH&EOS开发资源及工具集合(完整汇总版)

    ETH&EOS开发资源及工具集合(完整汇总版) 3113 ETH开发资源篇 一.开发语言 ·         Solidity - 官方推荐以太坊智能合约开发语言,也是目前最为主流的智能合约语 ...

  6. 义隆单片机学习笔记之(一) 硬件框架&资源下载

    参考网址: 点击链接或右键链接地址 (台湾义隆官网)http://www.emc.com.tw/chs/tech_8bit.asp (EM78P153K官方资料)http://www.emc.com. ...

  7. vue首页组件切换

    结构如下 代码如下: <template> <div id="page"> <div style="width: 100%" cl ...

  8. [转帖]POWER ISA开源 浪潮商用机器加速POWER技术生态建设步伐

    POWER ISA开源 浪潮商用机器加速POWER技术生态建设步伐 [原创]   2019-08-26 18:51:04 关键字: 开源 Power 浪潮商用机器 http://server.zhid ...

  9. 【Jmeter源码解读】002——程序入口类NewDriver.java

    1.运行环境的检查 2.刚才初始化的 classloader 加载类 org.apache.jmeter.JMeter 然后通过 java 反射的方式来调用 org.apache.jmeter.JMe ...

  10. (十五)mybatis 逆向工程

    目录 为什么需要逆向工程 使用方法 如何读懂生成的代码 总结 为什么需要逆向工程 对于数据库中的那么多的表 ,基本的 CRUD 操作 ,以及 mybatis 需要使用的 接口.mapper ,这些工作 ...