一.什么是线程池?
线程池可以说是存放和管理多个线程的池子。
二.为什么要用到线程池呢?
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秒。