zoukankan      html  css  js  c++  java
  • Java并发编程的艺术(十)——线程池

    线程池的作用

    1. 降低资源消耗。重复利用已有线程,减少线程的创建和销毁造成的消耗。
    2. 提高响应速度。当有任务需要处理的时候,就不用再花费重新创建线程的时间了。
    3. 提高线程的可管理性。不合理利用线程,会浪费资源,影响系统稳定,线程池可以对线程进行统一分配、调优和监控。

    线程池的实现原理

    线程池由两种觉得组成:多个工作线程和一个阻塞队列。

    • 工作线程:运行在线程池中,当需要执行任务的时候,就会被用来执行任务。线程池中还会被划分出一个核心线程池。
    • 阻塞队列:当核心线程池中的工作线程数量满了,新加入的任务就会进入阻塞队列。
      在这里插入图片描述
      执行流程:
      在这里插入图片描述

    线程池使用方法

    创建线程池

    new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
    
    • corePoolsize:线程池的基本大小,通过设置这个值,将线程池的线程数量维持在某一大小。

    • maximumPoolSize:最大线程数量,这个线程池可以运行的线程最大数量。如果阻塞队列满了,而最大线程数量没有满,那么线程池就会创建新的线程来执行任务。如果最大线程数满了,那么就无法执行任务。

    • keepAliveTime:空闲线程的存活时间,如果运行的线程数量超过核心线程数量,同时空闲的线程存活时间大于这个值,那么超时的空闲线程就会被销毁。如果任务很多,每个任务的执行时间很短,就可以调大这个值,以提高线程的利用率。

    • timeUnit:keepAliveTime的单位。

    • runnableTaskQueue:任务队列, 用于保存等待执行任务的阻塞队列,可以有一下几个选择:

      1. ArrayBlockingQueue:基于数组结构的阻塞队列,FIFO;
      2. LinkedBlockingQueue:基于链表,吞吐量高于前者,FIFO;
      3. SynchronousQueue:没有空间的存储队列,每一个插入操作都必须等到其他线程的调用移除,不然插入操作本身处于阻塞。吞吐量高于前者;
      4. PriorityBlockingQueue:具有优先级的无限阻塞队列。
    • handler: 饱和策略,如果实际运行的线程数达到最大值,则执行饱和策略,JDK提供下面4中策略:

      1. AbortPolicy:直接抛出异常,默认策略。
      2. CallerRunsPolicy:只用调用者所在的线程执行任务。
      3. DiscardOldestPolicy:丢弃任务队列中最久的任务。
      4. DiscardPolicy:丢弃新来的任务。

    任务提交

    提交任务的两种方法:

    • excute() :不需要返回值的任务。可以提交Runnable接口的任务。执行过程中无法抛出异常。

    • submit():需要返回值的。可以提交Callable接口的任务。然会Future对象,通过get获取。

    threadsPool.excute(new Runnable() {
    	@Override
    	public void run() {
    		dosometing();
    	}
    }
    
    Future<Object> future = executor.submit(harReturnValuetask);
    
    try{
    	Object s = future.get();
    } catch(InterruptedException e) {
    	dosomething();
    } catch(ExcutionException e) {
    	dosomething();
    } finally {
    	excutor.shutdown();
    }	
    

    关闭线程池

    两种方法关闭线程,都是通过遍历线程池中的工作线程,然后逐一调用interrupt方法来中断,无法响应中断的线程可能不能关闭。

    • shutdown():将线程池的状态改为关闭,中断没有执行任务的线程和取消阻塞线程中的等待任务,然后等待其他线程执行任务完毕之后才关闭线程。
    • shutdownNow():首先将线程池状态改为关闭,然后停止正在运行或没有运行的任务。

    合理配置线程池

    主要思想:根据任务的特性来配置线程池的参数。

    任务的硬件资源需求

    • CPU密集型任务
      配置尽可能小的线程池,如CPU核心数+1的线程池。这样可以减小切换上下文带来的开销。
    • IO密集型任务
      配置尽可能大的线程池,如CPU核心数*2的线程池。这样当线程任务在等待IO的时候,CPU可以做别的事情了。
    • 混合型任务
      将任务再分为CPU密集和IO密集,然后再用不同的线程池运行,只要两个任务执行的时间相差不是太大,分解后执行的吞吐量将高于串行执行的吞吐量。如果差距很大,时间短的任务会等待时间长的,还要加上任务拆分与合并的开销,更浪费资源。

    任务的优先级

    利用PriorityBlockingQueue来处理。让优先级高的任务先执行。

    任务的执行时间

    不同执行时间的任务交给不同规模的线程池,或者交给优先级队列,让时间短的先执行。

    任务的依赖性

    例如依赖于数据库连接池的任务,需要等待数据库的返回才能继续执行,所以,开多一点的线程数,这样提高CPU的使用效率。

    使用有界队列

    能增加系统的稳定性和预警能力。要设置大一点,这样在例如SQL操作的时候,可以有足够的线程可以等待,从而也不浪费资源。

  • 相关阅读:
    测试成功的Python中文文件读写脚本
    Solaris 10上Matlab 7(R14)安装手记
    人生:对心物互作的反应
    破解windows登录密码的有效方法
    Java实现二维码QRCode的编码和解码
    java调用储存过程的方法
    Java多态性的两个特殊情况
    Java中的TCP/UDP网络通信编程
    C语言之详解#ifdef等宏及妙用
    26个Jquery使用小技巧
  • 原文地址:https://www.cnblogs.com/lippon/p/14117659.html
Copyright © 2011-2022 走看看