定义
一种线程的使用模式,它可以管理整个项目线程任务,因为线程的创建和销毁会消耗大量的cpu资源,且线程不能无节制的开启占用系统运存,所以需要用一个对象来管理线程数量的使用,像池子一样有一定大小的容量限制和尽可能地减少线程的创建和销毁。
特点
1.一般一个项目就一个线程池,单例模式;
2.限制线程数量;
3.减少线程的创建和销毁。
如何定制池子大小
根据服务系统是CPU密集型还是I/O密集型,适当选择线程数量的多少,CPU运行速度一般都是比I/O数据流读写的速度快得多,所以CPU密集型的线程数量较少,一般池子限制的线程数为(CPU数量 + 1)个,而为了提高I/O的操作效率,减少CPU等待时间,I/O密集型限制的线程数为(2 ✖ CPU数量)个。
参数
安卓原生类ThreadPoolExecutor的输入参数如下:
参数名 | 作用 |
---|---|
int corePoolSize | 核心线程数的数量,这些线程即使处于空闲状态也不会被销毁 |
int maximumPoolSize | 线程池最大线程数量 |
long keepAliveTime | 空闲线程存活时间,即线程池里除了核心线程的其他线程 |
TimeUnit unit | 空闲线程存活时间单位,有7种(见表1) |
BlockingQueue<Runnable> workQueue | 阻塞队列,用于保存待执行的线程,常用的有3种(见表2) |
ThreadFactory threadFactory | 线程工厂,ThreadFactory是一个接口,该接口只包含如下方法:Thread newThread(Runnable r); |
RejectedExecutionHandler handler | 拒绝策略,RejectedExecutionHandler也是一个接口,该接口只包含如下方法:void rejectedExecution(Runnable r, ThreadPoolExecutor executor); 安卓原生提供了4种(见表3)实现 |
表1
时间单位TimeUnit可选参数 | 时间间隔大小 |
---|---|
TimeUnit.DAYS | 天 |
TimeUnit.HOURS | 小时 |
TimeUnit.MINUTES | 分钟 |
TimeUnit.SECONDS | 秒 |
TimeUnit.MILLISECONDS | 毫秒 |
TimeUnit.MICROSECONDS | 微秒 |
TimeUnit.NANOSECONDS | 纳秒 |
表2
阻塞队列BlockingQueue可选参数 | 作用 |
---|---|
LinkedBlockingQueue | 链表结构的阻塞队列,有任务时,一是当前线程数小于核心线程数,则新建核心线程数执行任务,二是当前线程数大于等于核心线程数,则任务进入该队列等待,该队列导致最大线程数的设置失效,因为总可线程数被限制在了核心线程数 |
SynchronousQueue | 同步队列,该队列接收到任务则直接处理,核心线程满了则继续新建非核心线程工作,所以一般设置最大线程数为一个很大的数Integer.MAX_VALUE,当达到最大线程数则执行拒绝策略 |
ArrayBlockingQueue | 数组结构的阻塞队列,使用该队列时,需指定数组大小。有任务时,一是当前线程数小于核心线程数,则新建核心线程数执行任务,二是当前线程数大于等于核心线程数,则任务进入该队列等待,三是当队列里的任务已满初始化时的数组大小,则新建非核心线程执行任务,直到线程数达到最大线程数时,则执行拒绝策略 |
表3
拒绝策略RejectedExecutionHandler可选参数 | 作用 |
---|---|
AbortPolicy | 抛出异常 |
CallerRunsPolicy | 线程池不shutdown的情况下,执行被拒绝任务的run方法 |
DiscardPolicy | 什么都不做 |
DiscardOldestPolicy | 抛弃最早进入队列的任务,尝试把被拒绝的任务加入队列 |
一般写法
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 线程池管理器
*/
public class ThreadPoolManager {
// 用单例模式对线程进行管理
private static volatile ThreadPoolExecutor singleTon = null;
// 获取可用CPU数量
private static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
// 以 CPU 密集型为例
private static final int CORE_POOL_SIZE = CPU_NUM + 1;
// 设置最大线程数等于核心线程数
private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE;
// 非核心线程存活时间100毫秒
private static final long KEEP_ALIVE_TIME = 100;
// 时间单位设置为毫秒
private static final TimeUnit UNIT = TimeUnit.MILLISECONDS;
// 初始化数组结构阻塞队列的大小为 5
private static final BlockingQueue<Runnable> WORK_QUEUE = new ArrayBlockingQueue<>(5);
// 自定义个线程创建工厂
private static final ThreadFactory THREAD_FACTORY = new MyThreadFactory();
// 使用安卓原生异常抛出策略
private static final RejectedExecutionHandler HANDLER = new ThreadPoolExecutor.AbortPolicy();
public static ThreadPoolExecutor getInstance() {
if (singleTon == null) {
synchronized (ThreadPoolManager.class) {
if (singleTon == null) {
singleTon = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE_TIME, UNIT,
WORK_QUEUE, THREAD_FACTORY, HANDLER);
}
}
}
return singleTon;
}
/**
* 自定义线程生成工厂,该类为
* 每个线程增加一个计数标识
*/
private static class MyThreadFactory implements ThreadFactory {
// 从1开始增长打印线程名
AtomicInteger atomicInteger = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, String.valueOf(atomicInteger.getAndIncrement()));
return thread;
}
}
}
测试方法
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
// 打印当前线程名字
System.out.println(Thread.currentThread().getName());
}
};
for (int i = 0; i < 10; i++) {
// 往线程池提交10次
ThreadPoolManager.getInstance().submit(runnable);
}
// 关闭线程池
ThreadPoolManager.getInstance().shutdown();
}
测试结果
2
4
7
3
1
5
6
5
8
9
JDK自带的线程池管理类
原生JDK自带四种基于ThreadPoolExecutor实现的线程池管理类,如果懒得自己去实现就直接用吧,如下,假如我们有一个Runnable对象runnable:
// 线程池核心线程数为 0 和最大线程数为 Integer.MAX_VALUE
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.submit(runnable);
// 线程池核心线程数和最大线程数为自定义数字,下面的例子为 3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
fixedThreadPool.submit(runnable);
// 线程池核心线程为自定义数字,下面的例子为5个,该线程池为周期性或定期执行任务,下面例子延迟1秒,每3秒执行一次
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(runnable,1, 2, TimeUnit.SECONDS);
// 线程池的核心线程和最大线程数均为 1
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.submit(runnable);