Java线程池详解

一、为什么使用线程池

  • 降低资源消耗,提高线程使用率,降低创建线程和销毁线程的消耗
  • 提高响应速度,线程来了,直接有线程可执行,而不是创建线程在执行
  • 提高线程的可管理性,线程是稀缺资源,使用线程池可以统一分配调优

二、七种线程池的核心参数

  • corePoolSize :核心线程数大小
  • maxinumPoolSize :最大线程数大小
  • keepAliveTime:表示超出核心线程数之外的线程的空闲存活时间
  • unit:表示超出核心线程数之外的线程的空闲存活时间的单位
  • workQueue:阻塞队列,用于存放待执行的队列
  • threadFactory :线程工厂,用来生产线程执行任务
  • handler:任务拒绝策略
 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

三、三种阻塞队列

1. 基于数组的先进先出队列,有界

  • new ArrayBlockingQueue<>(10)

2.基于链表的先进先出队列,无界

  • new LinkedBlockingQueue<>()

3.无缓冲的等待队列,无界

  • new SynchronousQueue<>()

四、四种拒绝策略

1. 默认,队列满了丢任务抛出异常

  • new ThreadPoolExecutor.AbortPolicy()

2. 队列满了丢任务不异常

  • new ThreadPoolExecutor.DiscardPolicy()

3. 将最早进入队列的任务丢弃,之后再尝试加入队列

  • new ThreadPoolExecutor.DiscardOldestPolicy()

4. 如果添加到线程池失败,那么当前线程会自己去执行该任务

  • new ThreadPoolExecutor.CallerRunsPolicy()

五、五种常见的线程池

  • Executors提供的四种线程池,但是阿里Java开发手册中强烈要求我们不允许使用Executors来创建线程池对象
		ExecutorService executorService = null;
		//单线程线程池
        executorService = Executors.newSingleThreadExecutor();
       	//固定大小线程池 
        executorService = Executors.newFixedThreadPool(10);
        //缓存线程池
        executorService = Executors.newCachedThreadPool();
        //定时调度池
        executorService = Executors.newScheduledThreadPool();
  • 通过ThreadPoolExecutor自定义创建线程池,阿里规范推荐使用自定义线程池
 	public static void main(String[] args) {
       ThreadPoolExecutor executor = new ThreadPoolExecutor(10,20 , 30,TimeUnit.SECONDS,new ArrayBlockingQueue<>(20));
    }

1.阿里为什么不推荐使用Executors创建线程池呢

  • newFixedThreadPool和newSingleThreadExecutor方法他们都使用了LinkedBlockingQueue的任务队列,LikedBlockingQueue的默认大小为Integer.MAX_VALUE
	public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
  • newCachedThreadPool和 newScheduledThreadPool在Executor中定义的最大线程池大小为Integer.MAX_VALUE。
	 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
	public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  • 通过分析源码阿里禁止使用Executors创建线程池的原因就是FixedThreadPool和SingleThreadPool的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
  • CachedThreadPool和ScheduledThreadPool允许的创建最大线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

六、线程池的执行流程

  • 当任务来时,如果核心线程有空闲线程,则交给空闲线程执行
  • 若核心线程池满了,将任务写入阻塞队列
  • 若阻塞队列满了,如果未达到最大线程池大小,则创建临时线程,执行该任务
  • 若最大线程数满了,则选择相应的拒绝策略

七、线程池的复用原理

  • 线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了Thread创建线程时的一个线程必须对应一个任务的限制
  • 在线程池中,同一个线程不断的从阻塞队列中获取任务,其核心原理是线程池对Thread进行了封装,并不是每次任务都会调用Thread.start()方法来创建新线程,而是让让每个线程去执行一个循环任务,不停的检查是否有新任务执行,如果有则直接执行,也就是任务中的run()方法,将run方法当成普通的方法进行执行

八、线程池的阻塞队列的作用是什么?为什么先添加到队列而不是先创建最大线程

  • 阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使线程进入wait状态,释放CPU资源
  • 阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一直占的CPU资源
  • 在创建新线程的时候,是要获取全局锁的,这个时候其他的就得阻塞,影像了整体效率
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值