不使用线程池的问题
ExecutorService
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 | 说明 | 备注 |
---|---|---|
corePoolSize | 指定线程池的线程数量(核心线程) | 不能小于0 |
maximumPoolSize | 指定线程池可支持的最大线程数 | 最大数量 >= 核心线程数量 |
keepAliveTime | 指定临时线程的最大存活时间 | 不能小于0 |
unit | 指定存活时间的单位(秒、分、时、天) | 时间单位 |
workQueue | 指定任务队列 | 不能为null |
threadFactory | 指定用哪个线程工厂创建线程 | 不能为null |
handler | 指定线程忙、任务满的时候,新任务来了如何做 | 不能为null |
临时线程什么时候创建?
什么时候会开始拒绝任务?
Executors
(线程池的工具类)调用方法返回不同特点的线程池对象方法名称 | 说明 |
---|---|
public static ExecutorService newCachedThreadPool() | 线程数量随着任务增加而增加, 如果线程任务执行完毕 且空闲了一段时间则会被回收掉 |
public static ExecutorService newFixedThreadPool(int nThreads) | 创建固定线程数量的线程池, 如果某个线程因为执行异常而结束, 那么线程池会补充一个新线程替代它 |
public static ExecutorService newSingleThreadExecutor() | 创建只有一个线程的线程池对象, 如果该线程出现异常而结束, 那么线程池会补充一个新线程 |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,可以实现在给定 的延迟后运行任务,或者定期执行任务 |
注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的。
package com.app.d8_thread_pool;
/**
定义线程任务类
*/
public class MyRunnable extends Thread{
/**
重写run方法(里面是线程任务)
*/
@Override
public void run() {
// 获取线程名称
String name = Thread.currentThread().getName();
for (int i = 1; i <= 3; i++) {
System.out.println(name + "线程输出了第" + i + "个HelloWorld!!");
}
// 模拟线程很忙的时候
try {
System.out.println(name + "线程与任务绑定了,线程进入休眠了~~");
Thread.sleep(100000000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.app.d8_thread_pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
目标:使用Executors工具类实现线程池。
它的底层也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的
*/
public class ThreadPoolDemo3 {
public static void main(String[] args) {
// 1、创建固定线程数量的线程池
ExecutorService pools = Executors.newFixedThreadPool(3);
// 2、创建线程任务交给线程池处理
pools.execute(new MyRunnable());
pools.execute(new MyRunnable());
pools.execute(new MyRunnable());
// 已超出固定线程池的数量范围,因此该任务不会执行!!
pools.execute(new MyRunnable());
}
}
方法名称 | 说明 |
---|---|
public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService newSingleThreadExecutor() |
允许请求的任务队列长度是 Integer.MAX_VALUE, 可能出现OOM错误(java.lang.OutOfMemoryError) |
public static ExecutorService newCachedThreadPool() public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) |
创建的线程数量最大上限是 Integer.MAX_VALUE, 线程数可能会随着任务1:1增长, 也可能出现OOM错误(java.lang.OutOfMemoryError) |
1、Executors工具类底层是基于什么方式实现线程池对象的?
2、Executors是否适合做大型互联网场景的线程池方案?
ExecutorService pools = new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(6),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
方法名称 | 说明 |
---|---|
void execute(Runnable command) | 执行任务/命令,无返回值,一般用来执行 Runnable 任务 |
Future< T > submit(Callable< T > task) | 执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务 |
void shutdown() | 等待全部任务执行完毕后关闭线程池 |
List< Runnable > shutdownNow() | 立刻关闭,停止正在执行的所有任务,并返回队列中未执行的任务 |
策略 | 说明 |
---|---|
ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛出 RejectedExecutionException 异常(是默认的策略) |
ThreadPoolExecutor.DiscardPolicy | 丢弃任务,但是不抛出异常(不推荐) |
ThreadPoolExecutor.DiscardOldestPolicy | 抛弃队列中等待最久的任务,然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy | 由主线程负责调用任务的 run() 方法从而绕过线程池直接执行(相当于是老板亲自服务) |
package com.app.d8_thread_pool;
/**
定义线程任务类
*/
public class MyRunnable extends Thread{
/**
重写run方法(里面是线程任务)
*/
@Override
public void run() {
// 获取线程名称
String name = Thread.currentThread().getName();
for (int i = 1; i <= 3; i++) {
System.out.println(name + "线程输出了第" + i + "个HelloWorld!!");
}
// 模拟线程很忙的时候
try {
System.out.println(name + "线程与任务绑定了,线程进入休眠了~~");
Thread.sleep(100000000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.app.d8_thread_pool;
import java.util.concurrent.*;
/**
目标:学习使用线程池处理 Runnable 任务
作用:线程池大大提高了线程的复用技术,不需要为每个新任务都创建一个新线程!!
*/
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 1、创建线程池
/*
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
*/
ExecutorService pools = new ThreadPoolExecutor(3, 5, 5, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(6), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
// 2、创建 Runnable 任务
Runnable target = new MyRunnable();
/**
3、将Runnable任务交给线程池处理
*/
// a.3个核心线程被占用
pools.execute(target);
pools.execute(target);
pools.execute(target);
// b.任务队列已满6个
pools.execute(target);
pools.execute(target);
pools.execute(target);
pools.execute(target);
pools.execute(target);
pools.execute(target);
// c.核心线程在忙,任务队列已满,开始创建临时线程
pools.execute(target);
pools.execute(target);
// d.3个核心线程 + 2个临时线程,已达到线程池最大线程数量,此时开始触发新任务拒绝策略
try {
pools.execute(target); // 抛出异常: RejectedExecutionException
} catch (Exception e){ // 捕获异常!
System.out.println("临时线程数量已超出线程池最大线程数量~~");
e.printStackTrace();
}
// 4、关闭线程池(开发中一般不会使用:因为无论是系统、网站、购物网等等,都是要一直处理每个用户发起的请求的。)
// pools.shutdownNow(); // 立即关闭线程池,即使任务没有完成,也会关闭,会丢失任务!!
// pools.shutdown(); // 等待所有任务执行完毕后再关闭线程池!!
}
}
临时线程数量已超出线程池最大线程数量~~
pool-1-thread-2线程输出了第1个HelloWorld!!
pool-1-thread-5线程输出了第1个HelloWorld!!
pool-1-thread-3线程输出了第1个HelloWorld!!
pool-1-thread-1线程输出了第1个HelloWorld!!
pool-1-thread-3线程输出了第2个HelloWorld!!
pool-1-thread-5线程输出了第2个HelloWorld!!
pool-1-thread-4线程输出了第1个HelloWorld!!
pool-1-thread-2线程输出了第2个HelloWorld!!
pool-1-thread-4线程输出了第2个HelloWorld!!
pool-1-thread-5线程输出了第3个HelloWorld!!
pool-1-thread-3线程输出了第3个HelloWorld!!
pool-1-thread-1线程输出了第2个HelloWorld!!
pool-1-thread-4线程输出了第3个HelloWorld!!
pool-1-thread-2线程输出了第3个HelloWorld!!
pool-1-thread-1线程输出了第3个HelloWorld!!
pool-1-thread-4线程与任务绑定了,线程进入休眠了~~
pool-1-thread-5线程与任务绑定了,线程进入休眠了~~
pool-1-thread-1线程与任务绑定了,线程进入休眠了~~
pool-1-thread-2线程与任务绑定了,线程进入休眠了~~
pool-1-thread-3线程与任务绑定了,线程进入休眠了~~
java.util.concurrent.RejectedExecutionException: Task Thread[Thread-0,5,main] rejected from java.util.concurrent.ThreadPoolExecutor@378bf509[Running, pool size = 5, active threads = 5, queued tasks = 6, completed tasks = 0]
at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2065)
at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:833)
at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1365)
at com.app.d8_thread_pool.ThreadPoolDemo1.main(ThreadPoolDemo1.java:51)
1、线程池如何处理Runnable任务?
package com.app.d8_thread_pool;
import java.util.concurrent.Callable;
/**
定义线程任务类
*/
public class MyCallable implements Callable<String> {
private int n; // 定义变量,用于存储一个数
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum = 0; // 定义变量,用于求和
// 计算1-n的和
for (int i = 1; i <= n; i++) {
sum += i;
}
// 返回线程执行任务的结果
return Thread.currentThread().getName() + "线程执行计算 1-" + n + " 的和,结果是:" + sum;
}
}
package com.app.d8_thread_pool;
import java.util.concurrent.*;
/**
目标:使用线程池处理 Callable 任务
作用:线程池大大提高了线程的复用技术,不需要为每个新任务都创建一个新线程!!
*/
public class ThreadPoolDemo2 {
public static void main(String[] args) throws Exception {
// 1、创建线程池
ExecutorService pools = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5), new ThreadPoolExecutor.AbortPolicy());
// 2、创建Callable线程任务并交给线程池处理
Future<String> s1 = pools.submit(new MyCallable(100));
Future<String> s2 = pools.submit(new MyCallable(200));
Future<String> s3 = pools.submit(new MyCallable(300));
Future<String> s4 = pools.submit(new MyCallable(400));
Future<String> s5 = pools.submit(new MyCallable(500));
Future<String> s6 = pools.submit(new MyCallable(600));
// 3、获取线程执行任务完毕后的结果并输出
// String rs = s1.get();
// System.out.println(rs);
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());
System.out.println(s6.get());
}
}
1、线程池如何处理Callable任务,并得到任务执行完成后返回的结果?