up:: 线程池简介为什么要使用线程池

说明:

(1) 本篇博客主要介绍线程池的构造函数,主要是其中的6个参数;


一:创建和停止线程概述;

(1) 我们创建线程池的时,需要线程池的构造函数;

● 但是,线程池的构造函数的参数比较复杂;我们需要了解各个参数的含义后,才能更好的使用构造函数去创建线程池;

(2) 自动创建比较方便;

● 但是,对于线程池来说,自动创建有一些弊端;有时,我们手动创建更好;

(3) 线程池中线程数量,应给怎么设置;

● 比如,我们有1000个任务,我们应该设置多少个线程比较合适呐;

(4) 停止线程池相对来说,比较简单;


二:线程池的构造方法:参数;

线程池构造方法的参数;

1.corePoolSize:核心线程数,maxPoolSize:最大线程数;

(1)简述;


(2)案例;(重要!)

比如,我们创建了一个线程池: corePoolSize:核心线程数设为5maxPoolSize:最大线程数设为了10workQueue队列设为了100

那么,会有如下过程:

● 线程池在完成初始化的时候,里面是没有线程的;只有,我们往线程池里面放任务的时候,其才会创建线程,去执行任务;

● 【线程池初始化的时候,线程池中没有线程;】→【如果,此时来了个任务,线程池就会创建一个线程;】→【如果后面又来了个任务,(无论线程池中是否有闲置的线程,只要,此时线程池中线程数5;)线程池还会创建一个线程;】→【按照这样的规则,如果来了5个任务后,线程池中的线程数就达到了5】→【线程池中的这5个线程,通常会一直存活:即,即使这5个线程都处于空闲状态,其也会一直存在】→【后面,如果线程池中的5个线程都在执行任务,没有空闲的线程;;;;但是,此时又来了一些任务,其会把任务放在workQueue队列中去,进行排队】→【如果任务一下来了很多,把队列也排满了;而且,任务继续来的话;】→【那么,线程池会判断,如果此时线程数<10:那么,线程池就会继续创建线程;】→【如果,队列也满了,线程数也达到了10;;;如果此时,任务继续来的话,这个任务就会被拒绝】;→【】


(3)思考;

(1) 如果【corePoolSize:核心线程数】和【maxPoolSize:最大线程数】相同:就可以创建一个固定大小的线程池;

(2) 我们之所以要区分【corePoolSize:核心线程数】和【maxPoolSize:最大线程数】:目的是希望线程池保持较少的线程数,只有在负载很大的时候,才去增加线程数量;

(3) 如果我们把【maxPoolSize:最大线程数】设置的很大:就表示,那么这个线程池,可以容纳任意数量的并发任务;

(4) 只有workQueue队列满的时候,如果继续来任务,才会去创建多于【corePoolSize:核心线程数】的线程;但如果,我们使用的是无界队列(比如LinkedBlockingQueue),那么我们就不会去创建多于【corePoolSize:核心线程数】的线程;

2. KeepAliveTime:保持存活时间;

(1) 如果线程池中线程数量,已经超过了【corePoolSize:核心线程数】;比如,【corePoolSize:核心线程数】是5,此时线程池中线程数量为8;那么线程池目前就超过了3个;等到后面,任务较少,不忙的时候,对于这3个冗余线程,我们要回收一下;

(2) 即,【如果线程池当前线程数,多于corePoolSize】&&【而且,多余的线程空闲了】&&【空闲的时间,超过了我们设置的KeepAliveTime】:那么这些多余的线程,就会被回收;

(3) 这是一个非常不错的机制,主要目的是:在线程过多冗余的时候,减少资源消耗;

(4.1) 需要强调一下,【corePoolSize:核心线程数】是不会被回收的;即,如果【corePoolSize:核心线程数】是5,此时线程池中的线程数是5,即使一直没有任务,这5个线程一直空闲;这5个线程也不会被回收;

(4.2) 核心线程通常不会回收,java核心线程池的回收由allowCoreThreadTimeOut参数控制,默认为false,若开启为true,则此时线程池中不论核心线程还是非核心线程,只要其空闲时间达到keepAliveTime都会被回收。但如果这样就违背了线程池的初衷(减少线程创建和开销),所以默认该参数为false。(参考自【终于搞清楚了,关于核心线程会不会回收】)

设置方法:

 
     static ThreadPoolExecutor executor=new ThreadPoolExecutor(8,16,0,TimeUnit.SECONDS,new LinkedBlockingQueue<(10));
 
 
     static {
         //如果设置为true,当任务执行完后,所有的线程在指定的空闲时间后,poolSize会为0
         //如果不设置,或者设置为false,那么,poolSize会保留为核心线程的数量
         executor.allowCoreThreadTimeOut(true);
     }

3. workQueue:任务存储队列;

(1) wordQueue工作队列;就是在介绍【corePoolSize:核心线程数】和【maxPoolSize:最大线程数】时,介绍到的队列;

(2) wordQueue通常有三种队列类型:

第一种:直接交接队列,SynchronousQueue;

● 如果,任务不会很多,我们只是把任务通过这个队列做一下简单的中转,就交给线程去处理;就可以使用这种队列;

● 这种队列,本身内部是没有容量的;即,这种队列是存不下任务的;

● 所以,如果我们使用了这种队列,我们的【maxPoolSize:最大线程数】就需要设置的大一点;因为,没有队列作为缓冲时,我们很容易就会创建新的线程,为了防止任务还不太多时就被拒绝,所以【maxPoolSize:最大线程数】要设置的大一点;

第二种:无界队列,LinkedBlockingQueue;

● 这种队列,不会被塞满;很显然,此时如果任务很多的,超过【corePoolSize:核心线程数】后,任务都会被放在这种队列中;那么,此时【maxPoolSize:最大线程数】就会失去意义;

● 但是,如果任务处理的速度跟不上任务提交的速度,那么这个队列中的内容就会越来越多;由此,就可能会造成内存的浪费或者是OOM异常;

第三种:有界队列,ArrayBlockingQueue;

● 这种队列,可以设置一个队列大小;那么此时【maxPoolSize:最大线程数】就有意义了;

4. threadFactory:线程工厂:线程池创建线程的工厂类;

(1.1) 即,这个参数,我们需要传一个【可以专门用来生成线程的工厂对象】;

(1.2) 传了一个工厂对象后,该线程池中的线程,都将会由这个threadFactory来创建;

(1.3) 使用我们自己指定的threadFactory创建线程时,我们可以根据自己的需要去设置线程名、线程组、线程优先级、是否是守护线程等;

(2.1) 如果,我们没有自己指定【生产线程的工厂】;也可以使用默认的Executors.defaultThreadFactory(),去创建线程;

(2.2) 使用默认的Executors.defaultThreadFactory()创建的线程,都在同一个线程组;并且,这些线程拥有同样的优先级(5);并且,都不是守护线程;

PS:守护线程:

(3) 不过,通常情况下,我们使用默认的Executors.defaultThreadFactory()去创建线程,就足够了;

5. Handler:任务拒绝策略;(待补充……)