参考链接
https://blog.csdn.net/u012702547/article/details/52259529
https://blog.csdn.net/wolf909867753/article/details/77500625
通常,在Android中使用线程的话,基本使用new Thread来创建线程
例如
new Thread(new Runnable() {
@Override
public void run() {
//耗时操作
}
}).start();
但是在Java中,创建线程是消耗资源的,频繁的创建和销毁线程是相当消耗资源的,为此,引入了线程池的概念。
使用线程池可以指定并发线程数,可以控制线程延时启动,可以设定线程阻塞超时(超时后从等待队列移除)等等,可以更好的控制线程,而不像new Thread一样,无法管控
一般情况下,任务的执行,主要时间消耗在如下几个部分
1.线程创建时间T1 2.任务等待线程执行时间T2 3.线程执行任务时间T3 4.线程销毁时间T4
通常,任务执行的时间取决于CPU的性能,所以T3相对可优化性不高,那么就智能优化T1 T2 T4了
我们可以通过合理创建线程数,减少不必要线程的创建销毁以减少T1 T4,合理维持线程空转(时刻等待任务,减少任务等待时间),以便有任务立刻执行,减小T2。后面我们会在实例中看到各种运用。
线程池主要有以下四个部分,以下摘自本文
1.线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
2.工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
3.任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
4.任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}
int corePoolSize:核心线程数,始终运行在线程池中的线程数,及时这些线程是空闲状态也不会销毁,除非指定了allowCoreThreadTimeOut参数
corePoolSize the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set
int maximumPoolSize:
maximumPoolSize the maximum number of threads to allow in the pool
线程池最大线程数
long keepAliveTime:
keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads
非核心线程空闲的超时时间,如果非核心线程任务执行完毕,空闲状态超过keepAliveTime,则会被GC。如果allowCoreThreadTimeOut被指定,该参数也指核心线程超时时间
TimeUnit unit:
unit the time unit for the {@code keepAliveTime} argument
keepAliveTime的单位,指以下几种
NANOSECONDS
MICROSECONDS
MILLISECONDS
SECONDS
MINUTES
HOURS
DAYS
BlockingQueue workQueue:
workQueue the queue to use for holding tasks before they are executed.
This queue will hold only the {@code Runnable} tasks submitted by the
{@code execute} method.
工作队列,当线程数达到maximumPoolSize时,新创建的线程会被放到workQueue中等待其他线程执行完毕
corePoolSize maximumPoolSize workQueue三者数量关系:
corePoolSize为核心线程数
maximumPoolSize为核心线程+非核心线程数
workQueue为线程等待数
任务执行的优先级是这样的:如果有空闲的核心线程,交给核心线程,否则放到workQueue中,如果workQueue已满,则看看非核心线程是否满了,未满,放入非核心线程,否则抛出异常。
也就是:当创建的线程数>(maximumPoolSize+workQueue),则线程数量超出上限,抛出异常
ThreadFactory threadFactory:
threadFactory the factory to use when the executor creates a new
thread
用来创建线程的ThreadFactory类型。ThreadFactory是一个接口,通常需要指定实现类。常见实现类有以下两个
Executors.DefaultThreadFactory
Executors.PrivilegedThreadFactory
通常只要使用默认的ThreadFactory即可,除非自定义线程池才会使用到自定义ThreadFactory。自定义ThreadFactory需要指定线程优先级,name,守护状态,ThreadGroup等参数。
RejectedExecutionHandler handler:
handler the handler to use when execution is blocked because the
thread bounds and queue capacities are reached
当线程池中的线程数量已经达到最大数,会拒绝接受task,抛出异常。
特性
参数较多,基本可以定义自己想要的线程池,下面介绍的几种线程池大多数都可以通过指定ThreadPoolExecutor的参数来实现相同的效果
例子
public static void main(String [] args){
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 5,
1, TimeUnit.SECONDS, new LinkedBlockingDeque(10));
for (int i = 0; i < 15; i++) {
final int INDEX = i;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CHJ"+"run: " + INDEX);
}
};
poolExecutor.execute(runnable);
}
}
创建线程池,核心线程3 非核心线程2(5-3),线程队列10(最大任务数15)
大家可能对LinkedBlockingDeque比较陌生,这其实是BlockingQueue的具体实现类。
BlockingQueue常见实现类:
ArrayBlockingQueue | 实现基于数组,需指定大小,线程遵循FIFO(先入先出) |
LinkedBlockingQueue | 实现基于链表,可以不指定大小,此时队列大小为Integer.MAX_VALUE(也可指定大小),线程遵循FIFO(先入先出) |
PriorityBlockingQueue | 实现基于priority heap,无大小限制(逻辑上),添加线程操作可能由于OutOfMemoryError而失败,排序方式依赖Comparable(意味着队列中的任务是实现了Comparable的) |
SynchronousQueue | 异步队列,可以提高程序性能,但如果需要保持同步,不要使用 |
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
可以看到,newFixedThreadPool指定了线程数,同时他只有核心线程。
线程的超时时间为0,说明核心线程即使在没有任务可执行的时候也不会被销毁(这样可让FixedThreadPool更快速的响应请求),最后的线程队列是一个LinkedBlockingQueue,但是LinkedBlockingQueue却没有参数,这说明线程队列的大小为Integer.MAX_VALUE(2的31次方减1),OK,看完参数,我们大概也就知道了FixedThreadPool的工作特点了,当所有的核心线程都在执行任务的时候,新的任务只能进入线程队列中进行等待,直到有线程被空闲出来。
特性
只有核心线程,且数目在初始化时就确定,workQueue不限大小。
例子
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 15; i++) {
final int INDEX = i;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CHJ"+"run: " + INDEX);
}
};
fixedThreadPool.execute(runnable);
}
上述例子创建了一个创建了一个拥有3个核心线程,workQueue大小为Integer最大值的线程池,for循环创建了15个任务,线程池执行时先将3个task放入核心线程,其余放到workQueue,之后核心线程执行完毕后,从队列取出task继续执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
可以看到,和FixedThreadPool极为类似,只不过SingleThreadExecutor只有一个核心线程
特性
只有一个核心线程
例子
public static void main(String [] args) throws InterruptedException{
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 15; i++) {
final int INDEX = i;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CHJ"+"run: " + INDEX);
}
};
singleThreadPool.execute(runnable);
}
}
上述例子创建了一个创建了一个拥有1个核心线程,workQueue大小为Integer最大值的线程池,for循环创建了15个任务,线程池执行时先将1个task放入核心线程,其余放到workQueue,之后核心线程执行完毕后,从队列取出task继续执行,每次只能执行一个任务。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
可以看到,CachedThreadPool没有核心线程,有Integer.MAX_VALUE个非核心线程,非核心线程60s超时,同时workQueue是SynchronousQueue,也就是支持任务异步
特性
没有核心线程,非核心线程数为Integer.MAX_VALUE,支持task异步
例子
public static void main(String [] args) throws InterruptedException{
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 15; i++) {
final int INDEX = i;
Runnable runnable = new Runnable() {
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CHJ"+"run: " + INDEX);
}
};
}
cachedThreadPool.execute(runnable);
}
上述例子创建了一个创建了一个拥有0个核心线程,非核心线程为Integer.MAX_VALUE,workQueue支持异步的线程池,for循环创建了15个任务,线程池执行时先将15个task放入workQueue,从队列取出task,使用非核心线程执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
可以看出,核心线程数可以指定,非核心线程数为Integer.MAX_VALUE,workQueue类型为DelayedWorkQueue
特性
可以指定任务完成后的等待时间
例子
public static void main(String[] args) throws InterruptedException {
ScheduledExecutorService scheduledThreadPool = Executors
.newScheduledThreadPool(2);
Runnable runnable = null;
runnable = new Runnable() {
public void run() {
System.out.println("Task start"
+ format(System.currentTimeMillis()));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("CHJ " + "run: " + " " + format(System.currentTimeMillis()) + "thread id"
+ Thread.currentThread().getName());
System.out.println("Task end" + format(System.currentTimeMillis()));
}
};
System.out.println("Task start all"
+ format(System.currentTimeMillis()));
// scheduledThreadPool.schedule(runnable, 1, TimeUnit.SECONDS);
// scheduledThreadPool.scheduleAtFixedRate(runnable, 1, 4,
// TimeUnit.SECONDS);
scheduledThreadPool.scheduleWithFixedDelay(runnable, 1, 3,
TimeUnit.SECONDS);
}
static String format(long time) {
SimpleDateFormat formatter = new SimpleDateFormat("HH时mm分ss秒");
Date date = new Date(time);
return formatter.format(date);
}
scheduledThreadPool.schedule(runnable, 1, TimeUnit.SECONDS);
延时1s启动线程
scheduledThreadPool.scheduleAtFixedRate(runnable, 1, 4,
TimeUnit.SECONDS);
延时1s后启动线程,之后不管任务何时完成,从启动线程开始,每隔四秒尝试重新执行任务(如果任务没有执行完成,向后顺延直到任务完成)
scheduledThreadPool.scheduleWithFixedDelay(runnable, 1, 3,
TimeUnit.SECONDS);
延时1s后启动线程,之后,任务完成后等待三秒,执行下一次任务
FixedThreadPool | 核心线程大小固定,线程等待队列大小为整型max,没有非核心线程 |
SingleThreadExecutor | 与FixedThreadPool极为类似,只有一个核心线程 |
CachedThreadPool | 没有核心线程,非核心线程数Integer.MAX_VALUE,支持task异步,线程空闲60s后自动回收 |
ScheduledThreadPool | 可以指定任务执行频率,推迟时间等等,任务调度十分方便 |