线程池做的工作只要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。它的主要特点为:线程复用;控制最大并发数;管理线程。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
在Java中提供了两种类型的线程池来供开发人员使用,分别是ThreadPoolExecutor和ScheduledThreadPoolExecutor。其中ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,类的UML图如下所示。ScheduledThreadPoolExecutor的功能和Java中的Timer类似,它提供了定时的去执行任务或者固定时延的去执行任务的功能,其功能比Timer更加强大。
/*
* 线程池:提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建于销毁线程的额外开销
* 提高了响应的速度
* 二.线程池的体系结构
* juc包中的.Executor:负责线程的使用和调度的根接口
* ----**ExecutorService**子接口:线程池的主要接口
* -----------ThreadPoolExecutor线程池的实现类
* -----------ScheduleExecutorService子接口,用于线程的调度
* ----------------ScheduleThreadPoolExcutor:继承了ThreadPoolExecutor,实现了ScheduleExecutorService接口
*
* 三.工具类:Executors
* newFixedThreadPool:创建固定大小的线程池
* newCaschedThreadPool:缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
*/
public class Demo01_ThreadPool {
public static void main(String args[]) throws InterruptedException {
//ExecutorService pool = Executors.newFixedThreadPool(5); //一个线程池中五个受理线程
//ExecutorService pool = Executors.newSingleThreadExecutor(); //一个线程池中一个受理线程
ExecutorService pool = Executors.newCachedThreadPool(); //一池n线程
// 模拟有十个顾客来银行办理业务,目前有五个员工(线程)
try {
for (int i = 0; i < 10; i++) {
TimeUnit.MILLISECONDS.sleep(300);
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "-->办理业务");
});
}
} finally {
pool.shutdown();
}
}
}
public class Demo06_ThreadPool_submit {
public static void main(String args[]) throws InterruptedException, ExecutionException {
ExecutorService pool = Executors.newCachedThreadPool(); //一池n线程
// 模拟有十个顾客来银行办理业务,目前有五个员工(线程)
List<Future> futures = new ArrayList<>();
try {
for (int i = 0; i < 10; i++) {
TimeUnit.MILLISECONDS.sleep(300);
Future<String> future = pool.submit(() -> {
System.out.println(Thread.currentThread().getName() + "-->返回随机数");
return new Random().nextInt() + "";
});
futures.add(future);
}
} finally {
pool.shutdown();
}
for (Future future : futures) {
System.out.println(future.get());
}
}
}
来源:《阿里巴巴Java开发手册》
【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM
public class Demo02_自定义线程池 {
public static void main(String args[]) throws InterruptedException {
System.out.println(Runtime.getRuntime().availableProcessors()); //cpu核数
ExecutorService pool = new ThreadPoolExecutor(
2, // corePoolSize
5, // maximumPoolSize=cpu核数+1or2
2L, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), // capacity
Executors.defaultThreadFactory(), // threadFactory
new ThreadPoolExecutor.DiscardOldestPolicy()); // 任务拒绝策略
try {
// 整个最大容纳数等于maximumPoolSize+队列容量=5+3=8
for (int i = 0; i < 10; i++) {
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "-->办理业务");
});
}
} finally {
pool.shutdown();
}
}
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 | 功能 |
---|---|
int corePoolSize | 线程池的核心线程数 |
int maximumPoolSize | 线程池的最大线程数 |
long keepAliveTime | 空闲线程的最大空闲时间 |
TimeUnit unit | 空闲时间的单位,TimeUnit是一个枚举值,它可以是纳秒、微妙、毫秒、秒、分、小时、天 |
BlockingQueue workQueue | 存放任务的阻塞队列,常用的阻塞队列有ArrayBolckingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityQueue |
ThreadFactory threadFactory | 创建线程的工厂,通常利用线程工厂创建线程时,赋予线程具有业务含义的名称 |
RejectedExecutionHandler handler | 拒绝策略。当线程池线程数超过最大线程数时,线程池无法再接收任务了,这个时候需要执行拒绝策略 |
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue: 一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue: 一个不存储元素的阻塞队列。
LinkedTransferQueue: 一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque: 一个由链表结构组成的双向阻塞队列。
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。 (默认)
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务。(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。