一.什么是线程池?

线程池可以说是存放和管理多个线程的池子。

 

二.为什么要用到线程池呢?

 1.未使用线程池的影响: 

     多任务情况下,多个线程的频繁创建会占用大量的资源,而多个线程的销毁,要频繁的调用gc,也会影响性能。

 2.使用线程池的好处:

   (1)对线程统一管理,避免资源浪费。

   (2)对线程进行复用避免频繁的创建和销毁线程。

 

三.线程池的使用:

 1.基本线程池ThreadPoolExecutor:

  基本创建线程池的方式,初始化一个线程池:

poolExecutor = new ThreadPoolExecutor(
        3,//  corePoolSize线程池中核心线程的数量
        5,//maximumPoolSize线程池中最大线程数量
        1,//keepAliveTime非核心线程的超时时长, 当闲置时间超过这个时间后被回收
        TimeUnit.SECONDS,//unit 时长的单位
        new LinkedBlockingDeque<Runnable>(128)//workQueue线程池中的任务队列,主要存储提交且尚未被执行的任务。

);

例子中我们设置了核心线程数量为3,最大核心线程为5.

我们写个循环来跑下这个线程池:

for (int i = 0;i<10;i++){
    final  int number = i;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(5000);
                Log.w(TAG,"running." + number);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    poolExecutor.execute(runnable);

}

log打印如下,通过看log的执行时间可以看出,每个5s打印三个数字,说明有三个核心线程在同时执行。

 

具体过程: 

(1)execute一个线程之后,如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。 

(2)execute一个线程之后,如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行。

(3)execute一个线程之后,如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。

(4)execute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采取饱和策略,并抛出RejectedExecutionException异常。

 说明:我们的例子中核心线程数为3所以会开启3个核心线程去执行,当核心线程数达到3后就会存入workQueue等待执行,所以就是每次打印3个数字。 

2.java中不推荐直接使用该方式来创建线程池,推荐使用Executors的工厂方法来创建线程池,java提供了五种功能不一样的线程池:

(1)FixedThreadPool(可重用固定线程数)

创建方法,只传入一个核心线程数的参数:

fixedThreadPool = Executors.newFixedThreadPool(5);

看下源码构造方法:

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

 

从源码中我们可以看出该线程池的特点:

传入核心线程数,最大线程数为核心线程数,说明无非核心线程,然后没有设置队列的大小,说明队列是不限制大小的。 

(2)CachedThreadPool(按需创建)

创建方法,无参数:

cachedThreadPool = Executors.newCachedThreadPool();

源码:

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

从源码中我们可以看出该线程池的特点:

1.核心线程数为0,无核心线程数

2.最大线程数最大,全部为非核心线程数

3.每个非核心线程的等待时间为60s

4.采用SynchronousQueue队列,等待队列不限制大小(SynchronousQueue是不存储元素的,每次插入操作必须伴随一个移除操作,一个移除操作也要伴随一个插入操作。) 

该线程池的弊端:因为非核心线程数量不做限制,所以有可能导致不断的创建线程,线程创建过多会出现oom. 

(3)SingleThreadPool(单个核心线程)

创建方法,无参数:

singleThreadPool = Executors.newSingleThreadExecutor();

源码:

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

从源码中可以看到只有一个核心线程数,无非核心线程数. 

所以用过log可以看出,该线程池每次只有一个线程在执行。 

(4)SingleThreadScheduledExecutor(单个核心线程,无限非核心线程)

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));

}

该线程方式同3一样是单个,区别在与非核心线程数不限制。 

(5)ScheduledThreadPool(定时延时执行)

创建,传入核心线程数为3。

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

scheduledThreadPool.schedule(runnable,5,TimeUnit.SECONDS);//传入定时5秒执行参数

 

 源码:

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

}

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

}

 

从log中我门看出,从点击开始后到第一次执行隔了15秒,因为我们延时了10秒在加上线程休眠5秒。