线程池
- 什么是线程池?
- 线程池就是一个可以复用线程的技术。
- 不使用线程池的问题
- 如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。
线程池实现的[[API]]、参数说明
-
谁代表线程池?
- JDK 5.0起提供了代表线程池的[[接口]]:ExecutorService
-
如何得到线程池对象
- 方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
-
方式二:使用Executors(线程池的工具类)调用方法返回不同特点的线程池对象
-
ThreadPoolExecutor构造器的参数说明
-
参数一:指定线程池的线程数量(核心线程): corePoolSize——>不能小于0
-
参数二:指定线程池可支持的最大线程数: maximumPoolSize——>最大数量 >= 核心线程数量
-
参数三:指定临时线程的最大存活时间: keepAliveTime——>不能小于0
-
参数四:指定存活时间的单位(秒、分、时、天): unit——>时间单位
-
参数五:指定任务队列: workQueue——>不能为null
-
参数六:指定用哪个线程工厂创建线程: threadFactory——>不能为null
-
参数七:指定线程忙,任务满的时候,新任务来了怎么办: handler——>不能为null
-
线程池常见面试题
- 临时线程什么时候创建啊?
- 新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
- 什么时候会开始拒绝任务?
- 核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝。
- 临时线程什么时候创建啊?
线程池处理Runnable任务
- ThreadPoolExecutor创建线程池对象示例
ExecutorService pool = new ThreadPoolExecutor(5,10,5,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
- ExecutorService的常用方法
方法名称 | 说明 |
---|---|
void execute(Runnable command) | 执行任务/命令,没有返回值,一般用来执行 Runnable 任务 |
Future<T> submit(Callable<T> task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务 |
void shutdown() | 等任务执行完毕后关闭线程池 |
List<Runnable> shutdownNow() | 立刻关闭,停止正在执行的任务,并返回队列中未执行的任务 |
- 让线程池执行没有返回值的任务
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(3,5,2,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//1.让线程池执行没有返回值的任务
A a = new A();
pool.execute(a);
pool.execute(a);
pool.execute(a);
pool.execute(a);
pool.execute(a);
pool.execute(a);
pool.execute(a);
pool.execute(a);
}
}
class A implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行没有返回值的任务");
}
}
-
结果为:
-
当执行的次数超过核心线程的3个和等待列表2个之后,就会使用两个临时线程,如果超出3+2+2后,就会上图的错误。
- 让线程池执行有返回结果的任务
//2.让线程池执行有返回结果的任务
B b = new B(100);
B b1 = new B(1000);
B b2 = new B(10000);
B b3 = new B(100000);
B b4 = new B(1000000);
long start = System.currentTimeMillis();
//多线程执行的时间 206毫秒 无睡眠时 4毫秒
Future<String> s1 = pool.submit(b);
Future<String> s2 = pool.submit(b1);
Future<String> s3 = pool.submit(b2);
Future<String> s4 = pool.submit(b3);
Future<String> s5 = pool.submit(b4);
System.out.println(s1.get());
System.out.println(s2.get());
System.out.println(s3.get());
System.out.println(s4.get());
System.out.println(s5.get());
//单线程执行的时间 506毫秒 无睡眠时 3毫秒
/*System.out.println(b.call());
System.out.println(b1.call());
System.out.println(b2.call());
System.out.println(b3.call());
System.out.println(b4.call());*/
long end = System.currentTimeMillis();
System.out.println("线程池执行花费的时间为:"+(end-start));
System.out.println("单线程执行花费的时间为:"+(end-start));
class B implements Callable<String> {
private int num;
public B(int num) {
this.num = num;
}
@Override
public String call() throws Exception {
int s = 0;
for (int i = 0; i < num; i++) {
s+=i;
}
Thread.sleep(100);
return Thread.currentThread().getName()+"计算【1——"+num+"】的和为:"+s;
}
}
-
在使用又返回结果方法时:当计算的业务复杂程度很小时,可以使用单线程,因为创建的线程少,但是当业务复杂度上升时,使用线程池时无疑是最好的选择。
-
新任务拒绝策略
策略 | 详解 |
---|---|
ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛出RejectedExecutionException[[异常]]。是默认的策略 |
ThreadPoolExecutor.DiscardPolicy: | 丢弃任务,但是不抛出异常 这是不推荐的做法 |
ThreadPoolExecutor.DiscardOldestPolicy | 抛弃队列中等待最久的任务 然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 由主线程负责调用任务的run()方法从而绕过线程池直接执行 |
- 线程池如何处理Runnable任务
- 使用ExecutorService的方法:void execute(Runnable target)
线程池处理Callable任务
- ExecutorService的常用方法
Executors工具类实现线程池
- Executors得到线程池对象的常用方法
- Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。
-
OOM:全称“Out Of Memory”,翻译成中文就是内存用完了,来源于
java.lang.OutOfMemoryError
。 -
注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。
-
Executors使用可能存在的陷阱
- 大型并发系统环境中使用Executors如果不注意可能会出现系统风险。