当前位置: 首页 > news >正文

Java之线程池深度剖析

1.线程池的引入
  引入的好处:
  1)提升性能。创建和消耗对象费时费CPU资源
  2)防止内存过度消耗。控制活动线程的数量,防止并发线程过多。
  使用条件:
     假设在一台服务器完成一项任务的时间为T
     T1 创建线程的时间    
     T2 在线程中执行任务的时间,包括线程间同步所需时间    
     T3 线程销毁的时间     
     显然T = T1+T2+T3。注意这是一个极度简化的假设。
     可以看出T1,T3是多线程本身的带来的开销,我们渴望减少T1,T3所用的时间,从而减少T的时间。但一些线程的使用者并没有注意到这一点,所以在程序中频繁的创建或销毁线程,这导致T1和T3在T中占有相当比例。显然这是突出了线程的弱点(T1,T3),而不是优点(并发性)。
     线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
     线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目。

     在Android中当同时并发多个网络线程时,引入线程池技术会极大地提高APP的性能。

2.线程池例子
 1)JDK自身带有线程池的实现类ThreadPoolExecutor
 2)下面是一个模拟ThreadPoolExecutor的例子,以加深对原理的理解

public final class ThreadPool {
     // 线程池中默认线程的个数为5
     private static int worker_num = 5;
     // 工作线程
     private WorkThread[] workThreads;
    
     // 任务队列,作为一个缓冲,List线程不安全
     private List<Runnable> taskQueue = new LinkedList<Runnable>();

     private static ThreadPool threadPool;

     // 创建具有默认线程个数的线程池
     private ThreadPool() {
          this(5);
     }

     // 创建线程池,worker_num为线程池中工作线程的个数
     private ThreadPool(int worker_num) {
          ThreadPool.worker_num = worker_num;
          workThreads = new WorkThread[worker_num];
          for (int i = 0; i < worker_num; i++) {
               workThreads[i] = new WorkThread();
               workThreads[i].start();// 开启线程池中的线程
          }
     }

     // 单态模式,获得一个默认线程个数的线程池
     public static ThreadPool getThreadPool() {
          return getThreadPool(ThreadPool.worker_num);
     }

     // 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
     // worker_num<=0创建默认的工作线程个数
     public static ThreadPool getThreadPool(int worker_num1) {
          if (threadPool == null)
               threadPool = new ThreadPool(worker_num1);
          return threadPool;
     }

     // 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
     public void addTask(Runnable task) {
          synchronized (taskQueue) {
               taskQueue.add(task);
               taskQueue. notifyAll();
          }
     }

     // 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
     public void destroy() {
          while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
               try {
                    Thread.sleep(10);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
          }
          // 工作线程停止工作,且置为null
          for (int i = 0; i < worker_num; i++) {
               workThreads[i].stopWorker();
               workThreads[i] = null;
          }
          threadPool=null;
          taskQueue.clear();// 清空任务队列
     }

     /**
      * 内部类,工作线程
      */
     private class WorkThread extends Thread {
          // 该工作线程是否有效,用于结束该工作线程
          private boolean isRunning = true;

          /*
           * 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
           */
          @Override
          public void run() {
               Runnable r = null;
               while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
                    synchronized (taskQueue) {
                         while (isRunning && taskQueue.isEmpty()) {// 队列为空
                              try {
                                   taskQueue.wait(20);
                              } catch (InterruptedException e) {
                                   e.printStackTrace();
                              }
                         }
                         if (!taskQueue.isEmpty())
                              r = taskQueue.remove(0);// 取出任务
                    }
                    if (r != null) {
                         r.run();// 执行任务
                    }
                    r = null;
               }
          }

          // 停止工作,让该线程自然执行完run方法,自然结束
          public void stopWorker() {
               isRunning = false;
          }
     }
}

转载于:https://www.cnblogs.com/devinzhang/p/3856200.html

相关文章:

  • POCO浅探
  • Dataset+TableAdapter _.net最终数据访问类出现? 我的心血显然被藐视了
  • Scrum实施日记 - 我可以问问题吗?
  • Design Patterns
  • 手机端雅安地震寻人整合项目
  • 香港身份证
  • UDDI(一)
  • 浅谈 XSS CSRF(转)
  • ansible笔记(2):管理清单配置详解
  • VS2015 Web应用程序发布
  • 《Java核心技术卷一》之 泛型
  • emacs 窗口控制
  • 如何在同一任务列表中显示我的任务及我所属组的任务
  • POJ2017-Speed Limit
  • springboot不占用端口启动
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • Angular数据绑定机制
  • github从入门到放弃(1)
  • go append函数以及写入
  • java 多线程基础, 我觉得还是有必要看看的
  • JavaScript标准库系列——Math对象和Date对象(二)
  • LintCode 31. partitionArray 数组划分
  • React-Native - 收藏集 - 掘金
  • Vue组件定义
  • 机器学习 vs. 深度学习
  • 基于游标的分页接口实现
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 技术胖1-4季视频复习— (看视频笔记)
  • 普通函数和构造函数的区别
  • 深入 Nginx 之配置篇
  • 原创:新手布局福音!微信小程序使用flex的一些基础样式属性(一)
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 正则表达式-基础知识Review
  • ​低代码平台的核心价值与优势
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #QT(TCP网络编程-服务端)
  • (C语言)fread与fwrite详解
  • (NO.00004)iOS实现打砖块游戏(十二):伸缩自如,我是如意金箍棒(上)!
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (二)WCF的Binding模型
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (七)微服务分布式云架构spring cloud - common-service 项目构建过程
  • (十六)Flask之蓝图
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .net Application的目录
  • .net framework 4.0中如何 输出 form 的name属性。
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .net开发引用程序集提示没有强名称的解决办法
  • .sh 的运行
  • @Autowired 与@Resource的区别
  • @Pointcut 使用