java线程深度解析(六)——线程池技术

2024-09-09 03:48
文章标签 java 线程 技术 深度 解析

本文主要是介绍java线程深度解析(六)——线程池技术,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

http://blog.csdn.net/Daybreak1209/article/details/51382604



一种最为简单的线程创建和回收的方法:

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. new Thread(new Runnable(){  
  2.              @Override  
  3.             public void run() {  
  4.                  while(true)  
  5.                  {  
  6.                      try {  
  7.                         Thread.sleep(1000);//休息1s  
  8.                     } catch (InterruptedException e) {  
  9.                         e.printStackTrace();  
  10.                     }  
  11.                  }  
  12.             }  
  13.          }).start();  

     在run结束后,自动回收该线程 。但在真实生产环境中,可能会开启多个线程来支撑应用系统。创建和关闭都需要花费时间,并且再生产环境中,线程的数量必须合理控制,盲目的大量创建线程,对系统性能是有伤害的。为了节省多线程并发时不断创建和销毁线程所带来的额外开销,引入线程池。

     线程池的基本功能就是对线程进行复用。当接收一个任务等待处理时,先去线程池中查找是否有空闲线程,没有再创建新的线程;并且执行完毕后,将新建的线程放入线程池的空闲队列备用,而不直接销毁。

1、创建一个简易线程池

ThreadPool类:提供线程池创建、停止、添加线程、执行任务等方法

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. public class ThreadPool {  
  2.     private static ThreadPool instance=null;  
  3.     //空闲的县城队列  
  4.     private List<PThread> idleThreads;  
  5.     //已有的线程数  
  6.     private int count;  
  7.     private boolean isShutDown=false;  
  8.       
  9.     private ThreadPool()  
  10.     {  
  11.         this.idleThreads=new Vector(5);//类似于Arraylist  
  12.         count=0;  
  13.     }  
  14.     public int getCreatedThread()  
  15.     {  
  16.         return count;  
  17.     }  
  18.       
  19.     //取得线程实例  
  20.     public synchronized static ThreadPool getInstance()  
  21.     {  
  22.         if(instance==null)  
  23.             instance=new ThreadPool();  
  24.             return instance;  
  25.   
  26.     }  
  27.     //将线程放入线程池  
  28.     public synchronized void putThread(PThread putThread)  
  29.     {  
  30.         if(!isShutDown)  
  31.         {  
  32.             idleThreads.add(putThread);  
  33.         }else  
  34.         {  
  35.             putThread.shutDown();  
  36.         }  
  37.     }  
  38.       
  39.     //停止池中的所有线程  
  40.     public synchronized void shotdown()  
  41.     {  
  42.         isShutDown=true;  
  43.         for(int threadIndex=0;threadIndex<idleThreads.size();threadIndex++)  
  44.         {  
  45.             PThread idelThread=(PThread)idleThreads.get(threadIndex);  
  46.             idelThread.shutDown();  
  47.         }  
  48.     }  
  49.     //执行任务  
  50.     public synchronized void start()  
  51.     {  
  52.         PThread thread=null;  
  53.         //如果有空闲线程,直接使用  
  54.         if(idleThreads.size()>0)  
  55.         {  
  56.             int lastIndex=idleThreads.size()-1;  
  57.             thread=(PThread)idleThreads.get(lastIndex);  
  58.             idleThreads.remove(lastIndex);  
  59.             //立即执行这个任务  
  60.             thread.setTarget(thread);  
  61.         }else  
  62.         {  
  63.             //没有空闲线程,自己创建  
  64.             count++;  
  65.             thread=new PThread(target,count,this);  
  66.             //启动这个线程  
  67.             thread.start();  
  68.         }  
  69.               
  70.     }  
  71. }  

PThread类:模拟一个线程添加到线程池中,该线程只要不手动关闭,会一直等待任务并执行。

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. public class PThread extends Thread{  
  2.     //线程池  
  3.     private ThreadPool pool;  
  4.     //任务  
  5.     private Runnable target;  
  6.     private boolean isShutDown=false;  
  7.     private boolean isIdel=false;  
  8.       
  9.     //构造函数  
  10.     public PThread(Runnable target,String name,ThreadPool pool)  
  11.     {  
  12.         super(name);  
  13.         this.pool=pool;  
  14.         this.target=target;  
  15.     }  
  16.     public  Runnable  getTarget() {  
  17.         return target;  
  18.     }  
  19.     public boolean isIdle()  
  20.     {  
  21.         return isIdel;  
  22.     }  
  23.       
  24.     @Override  
  25.     public void run()  
  26.     {  
  27.         //只要不关闭,就一直不结束该线程  
  28.         while(!isShutDown)  
  29.         {  
  30.             isIdel=false;  
  31.             if(target !=null)  
  32.             {  
  33.                 //运行任务  
  34.                 target.run();  
  35.             }  
  36.             //任务结束,到闲置状态  
  37.             isIdel=true;  
  38.             try {  
  39.                 //任务结束后,不关闭该线程,而放入线程池中闲置备用  
  40.                 pool.putThread(this);  
  41.                 synchronized (this) {  
  42.                     //线程空闲,等待任务到来  
  43.                     wait();  
  44.                 }  
  45.           
  46.             } catch (Exception e) {  
  47.             }  
  48.             isIdel=false;  
  49.         }  
  50.     }  
  51.   
  52.     public synchronized void setTarget(Runnable newTarget)  
  53.     {  
  54.         target=newTarget;  
  55.         //设置任务之后,通知run方法,执行该任务  
  56.         notifyAll();  
  57.     }  
  58.     //关闭线程  
  59.     public synchronized void shutDown()  
  60.     {  
  61.         isShutDown=true;  
  62.         notifyAll();  
  63.     }  
  64. }  

MyThread任务对象 and Client调用

2、JDK1.6中的内置线程池——Executor框架

       JDK中提供了一套Executor框架支持更好的控制多线程操作。在concurrent包中是并发线程的核心,其中ThreadPoolExecutor表示一个线程池,Executors扮演者线程池工厂的角色,通过Executors可以创建特定功能的线程池。

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1.              //1、new一个固定线程数量的线程池,有一个新任务提交时,从该线程池中查找是否有空闲线程,如果没有新的任务会被暂存于一个任务队列中,待有空闲线程后再执行。  
  2. ExecutorService threadPoolFix=Executors.newFixedThreadPool(3);//每次3个线程,执行3个任务;其他任务等待。     
  3. //2、缓存线程池--返回一个跟任务数相等的线程数量,有多少任务就产生多少个线程进行处理。动态变化-干完了定期回收  
  4. ExecutorService threadPoolCache=Executors.newCachedThreadPool();  
  5.   
  6. //3、只有一个线程的线程池,任务按照任务队列顺序FIFO执行,保证线程池始终中有一个线程,当前死了,立马搞一个顶替工作  
  7. ExecutorService threadOne=Executors.newSingleThreadExecutor();  
  8.   
  9. //4、线程池大小为1,支持在给定时间执行某任务,如在某个固定的延时之后执行,或者周期性执行。-定时器效果  
  10. ExecutorService threadSingleScheduled=Executors.newSingleThreadScheduledExecutor();  
  11.   
  12. //5、指定一定固定大小的线程池  
  13. ExecutorService threadScheduled=Executors.newScheduledThreadPool(5);  

3、线程池优化

     线程池的大小对系统性能有着很大影响,过大过小都不利于程序运行。具体多大的线程池比较合适,可以借鉴《Java并发编程实战》中提供的一个计算公式:最优线程数量=CPU的数量*CPU的使用率*[1+(等待时间/计算时间)]。在java中通过Runtime.getRuntime().availableProcessors();获取可用CPU

4、自定义线程池

      jdk创建线程池对象大多继承于一个核心线程池类ThreadPoolExecutor,当采用自定义方式创建线程池时,也主要对这个类进行再封装。下面是ThreadPoolExecutor的核心构造方法:

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. public ThreadPoolExecutor(int corePoolSize,//线程池中线程数量  
  2.                           int maximumPoolSize,//线程池中最大线程数  
  3.                           long keepAliveTime,//当池中线程数超过corePoolSize时,多余线程多长时间内会被销毁  
  4.                           TimeUnit unit,//keepAliveTime的时间单位  
  5.                           BlockingQueue<Runnable> workQueue, //任务队列     
  6.                           RejectedExecutionHandler handler //拒绝策略,当任务太多时,如何拒绝任务) {  
  7.     this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,  
  8.          Executors.defaultThreadFactory(), handler);  
  9. }    

任务队列workqueue用于存放Runnable对象,主要有以下几种实现:

1、直接提交的队列

2、有界的任务队列

3、无界的任务队列

4、优先任务队列

拒绝策略:

1、AbortPolicy:直接抛出异常,系统停止工作

2、CallerRunsPolicy:只要线程池未关闭,直接再运行被抛弃的任务

3、DiscardOledestPolicy:丢弃最老的一个任务请求

4、DiscardPolicy:丢弃无法处理的任务,不予处理

下面模拟一个任务排序基于优先任务队列:

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. public class DefinePool implements Runnable,Comparable<DefinePool>{  
  2.     protected String name;  
  3.       
  4.     /*  
  5.      * 构造方法  
  6.      */  
  7.     public DefinePool() {  
  8.           
  9.     }  
  10.     public DefinePool(String name) {  
  11.         this.name=name;  
  12.     }  
  13.   
  14.     /*  
  15.      * 模拟任务       
  16.      */  
  17.     @Override  
  18.     public void run() {  
  19.         try {  
  20.             Thread.sleep(1000);  
  21.             System.out.println("模拟任务!!!!!!name:"+name);  
  22.         } catch (InterruptedException e) {  
  23.             e.printStackTrace();  
  24.         }  
  25.     }  
  26.       
  27.     /*  
  28.      * 比较任务优先级  
  29.      */  
  30.     @Override  
  31.     public int compareTo(DefinePool o) {  
  32.         //前提:线程名称标记优先级  
  33.         int me=Integer.parseInt(this.name.split("_")[1]);  
  34.         //System.out.println(me);  
  35.           
  36.         int other=Integer.parseInt(o.name.split("_")[1]);  
  37.           
  38.         if(me>other) return 1;  
  39.         if(me<other) return -1;  
  40.         else    return 0;  
  41.     }  
  42. }  

客户端调用类:

[html]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. public class DefineClient {  
  2.     public static void main(String[] args) {  
  3.         ExecutorService exe = new ThreadPoolExecutor(100, 200, 0L,  
  4.                 TimeUnit.SECONDS, new PriorityBlockingQueue<Runnable>());  
  5.         for(int i=0;i<1000;i++)  
  6.         {  
  7.             exe.execute(new DefinePool("testPoolext_"+Integer.toString(999-i)));  
  8.         }  
  9.     }  
  10. }  

最终输出:

模拟任务!!!!!!name:testPoolext_914
模拟任务!!!!!!name:testPoolext_915
模拟任务!!!!!!name:testPoolext_916
模拟任务!!!!!!name:testPoolext_0

模拟任务!!!!!!name:testPoolext_1
模拟任务!!!!!!name:testPoolext_3
模拟任务!!!!!!name:testPoolext_5
模拟任务!!!!!!name:testPoolext_7
模拟任务!!!!!!name:testPoolext_6
模拟任务!!!!!!name:testPoolext_4
模拟任务!!!!!!name:testPoolext_2
模拟任务!!!!!!name:testPoolext_8
模拟任务!!!!!!name:testPoolext_12
模拟任务!!!!!!name:testPoolext_11
模拟任务!!!!!!name:testPoolext_10
模拟任务!!!!!!name:testPoolext_17
模拟任务!!!!!!name:testPoolext_16
模拟任务!!!!!!name:testPoolext_14
模拟任务!!!!!!name:testPoolext_15
模拟任务!!!!!!name:testPoolext_20
     从加粗部分为分界,前期程序刚启动,无需使用等待队列,故从任务序号999往下执行;当线程池中线程全部占用,任务开始进入任务队列,排队执行,使用优先任务队列,任务按照程序预定的从1~999优先级依次降低的顺序,将任务按优先级顺序依次放入任务队列。FIFO那么最先执行的就是最先放入队列的高优先级任务。


这篇关于java线程深度解析(六)——线程池技术的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1150079

相关文章

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多