Java线程池

Java线程池

并发编程的一种编程方式是把任务拆分为一系列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe)。Executor在执行时使用内部的线程池完成操作。

1. 为什么要使用线程池
  1. 每个线程可用重复利用,能够减少创建和销毁线程的次数,减少系统开销。
  2. 可以根据系统承载力,调整线程池中工作线程的数目,防止过多消耗内存。
2. Java中的线程池框架
  1. 顶级接口:Executor,严格说Executor并不是一个线程池,而是执行线程的工具。真正的线程池接口是ExecutorService。
  2. ExecutorService:真正的线程池接口。
  3. ScheduledExecutorService:提供重复执行、延迟执行功能的线程池接口。
  4. ThreadPoolExecutor:ExecutorService的默认实现。
  5. ScheduledThreadPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
3. 线程池构造函数的各属性说明
  1. 核心池的大小,即线程池中允许空闲的线程数量。

  2. maximumPoolSize:线程池允许的最大线程数。在创建线程后线程池中线程数量为0,当有任务到来时创建一个线程去执行。当线程池中线程数量为corePoolSize时,将任务加入缓存队列,若入队失败(队列已满),则尝试创建临时线程,但临时+核心线程总数不能超过maximumPoolSize,当线程总数达到后会拒绝新任务。因此有两种方式可以让任务不被拒绝:① 将maximumPoolSize设为Integer.MAX_VALUE(线程不可能达到这个值),CachedThreadPool即是如此;② 使用无限容量的阻塞队列(比如LinkedBlockingQueue),FixedThreadPool即是如此。

  3. keepAliveTime:表示线程没有任务执行时最多保持多久时间。默认只有当线程池中线程数大于corePoolSize时,keepAliveTime才会起作用。但是如果调用了allowCoreThreadTimeOut(true)方法,在线程池中线程数不大于corePoolSize时,keepAliveTime也会起作用,直到线程数为0。

  4. unit:参数keepAliveTime的单位,有7种取值:

    TimeUnit.DAYS;				//天
    TimeUnit.HOURS;				//小时
    TimeUnit.MINUTES;			//分钟
    TimeUnit.SECONDS;			//秒
    TimeUnit.MILLISECONDS;		//毫秒
    TimeUnit.MICROSECONDS;		//微秒
    TimeUnit.NANOSECONDS;		//纳秒
    
  5. workQueue:阻塞队列(BlockingQueue接口的实现类),用来存储等待执行的任务,只保留execute方法提交的Runnable任务。一般有以下几种选择:

    • ArrayBlockingQueue:数组实现的阻塞队列,不支持扩容,队列满了会根据handler参数中指定的策略拒绝任务。
    • LinkedBlockingQueue:链表实现的阻塞队列,默认容量Integer.MAX_VALUE,也可以通过构造方法限制容量。
    • SynchronousQueue:零容量的同步阻塞队列,直到有线程接受任务才返回,用于生产者消费者同步问题,被称为同步队列。
    • PriorityBlockingQueue:二插堆实现的优先级阻塞队列。
    • DelayQueue:延时阻塞队列,队列中所有的任务都会封装成ScheduledFutureTask对象。
  6. threadFactory:线程工程,主要用来创建线程;默认使用Executors工具类定义的DefaultThreadFactory。可以实现ThreadFactory接口来自己控制创建线程的过程。

  7. handler:表示拒绝任务时的策略,有以下四种取值(默认为AbortPolicy):

    • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务
    • ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
4. 创建线程池

配置线程池较为复杂,Executors(注意这个有s不是Executor接口)类中提供了一些静态工厂用于创建常用的线程池。

  1. newSingleThreadExecutor:创建一个单线程的线程池。能保证所有任务的执行顺序按照任务的提交顺序执行。
  2. newFixedThreadPool:创建固定大小(corePoolSize=maximumPoolSize)的线程,拥有不限制大小的阻塞队列(LinkedBlockingQueue)。每次提交一个任务就创建一个线程,直到线程数量达到corePoolSize,之后的任务全部加入阻塞队列。
  3. newCachedThreadPool:创建核心线程数为0,不限制大小,临时线程60s回收的线程池,线程池大小依赖于操作系统(或JVM)能够创建的最大线程大小,使用同步队列,将任务直接提交给线程。
  4. newScheduledThreadPool:创建一个大小无限,临时线程用完就回收的线程池。此线程池支持定时及周期性执行任务的需求。
5. 任务提交的三种方式
  1. execute(Runnable command):定义在Executor接口中。
  2. submit的三个重载方法:定义在ExecutorService接口中。
  3. invoke(invokeAll,invokeAny)提交方式:定义在ExecutorService接口中

submit最终也是调用execute去执行,但是submit方法返回一个FutureTask对象,可以通过FutureTask对象获取任务执行的结果,

6. 更多种类的线程池 MoreExecutors

略,详解见此文档

参考资料
  1. 深入学习java源码之Executors.newFixedThreadPool()与Executors.newCachedThreadPool()
  2. Java 四种线程池的使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值