并发编程之java线程池

使用线程池有什么好处?

线程复用,避免了线程的重复创建销毁带来的效率上的影响;

控制并发线程的数量;

对线程做一些简单的管理(设置线程的状态);

 

ThreadPoolExecutor的重要参数

java中的线程池都是基于ThreadPoolExecutor类来实现的。

corePoolSize:核心线程数

  • 核心线程会一直存活,即使没有任务需要执行
  • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
  • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

queueCapacity:任务队列容量(阻塞队列)

  • 当核心线程数达到最大时,新任务会放在队列中排队等待执行

maxPoolSize:最大线程数

  • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
  • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

keepAliveTime:线程空闲时间

  • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
  • 如果allowCoreThreadTimeout=true,则会直到线程数量=0

allowCoreThreadTimeout:允许核心线程超时

rejectedExecutionHandler:任务拒绝处理器

  • 当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务
  • 当线程池被调用shutdown()后,后来的所有线程都会被拒绝

线程池的执行过程

当线程数小于核心线程数时,创建线程。

当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

当线程数大于等于核心线程数,且任务队列已满:

  1. 若线程数小于最大线程数,创建线程
  2. 若线程数等于最大线程数,抛出异常,拒绝任务

拒绝策略:

  1. AbortPolicy 丢弃任务,抛运行时异常
  2. DiscardOldestPolicy 工作队列头部的任务会被踢出队列,然后重试执行程序
  3. DiscardPolicy也是丢弃任务,只不过他不抛出异常。
  4. CallerRunsPolicy 执行任务,这个策略是不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行

常见的四种JDK提供的线程池

(1)可缓存线程池CachedThreadPool()

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

根据源码可以看出:

这种线程池内部没有核心线程,线程的数量是有没限制的。

在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。

没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。

适用:执行很多短期异步的小程序或者负载较轻的服务器。

(2)FixedThreadPool 定长线程池

public static ExecutorService newFixedThreadPool(int nThreads) {
    	return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
    }

根据源码可以看出:

该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。

如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。

适用:执行长期的任务,性能好很多。

(3)SingleThreadPool

public static ExecutorService newSingleThreadExecutor() {
      return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
    }

根据源码可以看出:

有且仅有一个工作线程执行任务

所有任务按照指定顺序执行,即遵循队列的入队出队规则。

适用:一个任务一个任务执行的场景。

(4)ScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    	return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    //ScheduledThreadPoolExecutor():
    public ScheduledThreadPoolExecutor(int corePoolSize) {
       super(corePoolSize, Integer.MAX_VALUE,
          DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
          new DelayedWorkQueue());
    }

根据源码可以看出:

DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是CachedThreadPool和FixedThreadPool 结合了一下。

不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。

这个线程池是上述4个中唯一一个有延迟执行和周期执行任务的线程池。

 

最大线程数设置

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值