目录
线程池
三种方法创建线程池
java
package com.qcby.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* demo1
* 了解了线程池的三大方法
*/
public class Demo1 {
public static void main(String[] args) {
// ExecutorService pool = Executors.newSingleThreadExecutor();// 单个线程的线程池
ExecutorService pool = Executors.newFixedThreadPool(5); //创建固定大小的线程池
// ExecutorService pool = Executors.newCachedThreadPool(); //可以伸缩的线程池
try {
for (int i = 1; i <= 10; i++) {
final int temp = i;
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "------>" + temp);
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
pool.shutdown();
}
}
}
单例的线程池

固定大小的线程池

可伸缩的线程池

看源码
java
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
它们三个的本质都是调用了ThreadPoolExecutor
由它们的构造器当中可以看到,newSingleThreadExecutor和newFixedThreadPool(默认的队列最大长度),它们允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量请求,导致OOM;CachedThreadPool允许创建线程的数量是Integer.MAX_VALUE(因为SynchronousQueue没有容量,要插入必须等待别的线程对里面的请求进行移除,不会产生堆积),可能会创建大量的线程,导致OOM.
ThreadPoolExecutor()的七个参数
它的构造器有7个参数:
|-----------------|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 参数名 | 含义 | 作用 |
| corePoolSize | 核心线程数量 | 当提交一个新任务时,如果当前运行的线程数少于 corePoolSize,即使有空闲线程,线程池也会创建一个新线程来处理该任务。 |
| maximumPoolSize | 最大线程数量 | 当任务队列(workQueue)满了之后,如果当前运行的线程数小于 maximumPoolSize,线程池会继续创建新的线程来处理任务(直到达到这个上限) |
| keepAliveTime | 线程空闲存活时间 | 如果线程池中的线程数超过了 corePoolSize,并且某个线程的空闲时间达到了 keepAliveTime,该线程会被回收销毁(直到线程数回到 corePoolSize) |
| unit | keepAliveTime参数的时间单位 | ---- |
| workQueue | 任务队列 | 用于存放等待执行的任务的阻塞队列,当线程池中的线程数已达到corePoolSize时,新提交的任务会先被放入这个队列中等待。 |
| threadFactory | 用于创建新线程的工厂 | 可以自定义它来设置线程的名称(方便调试)、优先级、是否为守护线程,或者统一记录线程创建日志。 默认值:如果不传,会使用 Executors.defaultThreadFactory(),生成的线程名称为 pool-{number}-thread-{number}。 |
| handler | 当任务无法被接受时触发的处理策略 | 当线程池已关闭,或线程数已达到 maximumPoolSize 且任务队列已满时。 内置策略 (均实现自 RejectedExecutionHandler): AbortPolicy (默认):直接抛出 RejectedExecutionException。 CallerRunsPolicy :由调用者(提交任务的线程)自己执行该任务。 DiscardPolicy :直接丢弃该任务,不抛异常。 DiscardOldestPolicy:丢弃队列中最旧的任务,然后重试提交当前任务。 |
java
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
创建线程池:
java
package com.qcby.pool;
import java.util.concurrent.*;
/**
* 玩线程池
* 模拟银行办理业务场景
*/
public class Demo2 {
public static void main(String[] args) {
ThreadPoolExecutor executor = null;
try {
//创建了一个线程池 核心线程2 最大线程5 阻塞队列容量3 空闲线程存活5s 默认的线程工厂和拒绝策略
executor = new ThreadPoolExecutor(2, 5,
5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 8; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "处理业务............");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
executor.shutdown();
}
}
}
我们创建了一个核心线程2,最大线程5,阻塞队列容量3 ,空闲线程存活5s 和使用默认的线程工厂和拒绝策略的线程池。
执行过程:
for循环一个创建了8个任务
前面两个任务创建了线程1和线程2
执行第三个任务时,由于核心线程数已达上限,创建了临时线程3执行
第四第五个任务创建了临时线程4和5
第六个任务时,最大线程数达到上限,丢入阻塞队列
第七第八同理丢入
等待其他线程执行完任务之后,再从阻塞队列中取出任务执行

当我们创建9个任务呢?
AbortPolicy策略

此时拒绝策略生效,AbortPolicy会直接抛出异常,并且把这个任务丢弃
| 拒绝策略 | 是否抛异常 | 是否丢弃任务 | 说明 |
|---|---|---|---|
AbortPolicy |
是 | 是 | 直接抛异常,需要上层处理 |
CallerRunsPolicy |
否 | 否 | 调用者线程执行,不会丢 |
DiscardPolicy |
否 | 是 | 静默丢弃,无感知 |
DiscardOldestPolicy |
否 | 是 | 丢弃最老的,提交新的 |
CallerRunsPolicy策略
CallerRunsPolicy会让调用者线程执行任务,但是main方法会被阻塞,会在所有任务提交完成(剩余任务能被线程池里面的线程处理的时候)后停止阻塞
java
package com.qcby.pool;
import java.util.concurrent.*;
/**
* 玩线程池
* 模拟银行办理业务场景
*/
public class Demo2 {
public static void main(String[] args) {
ThreadPoolExecutor executor = null;
try {
//创建了一个线程池 核心线程2 最大线程5 阻塞队列容量1 空闲线程存活5s 默认的线程工厂和拒绝策略
executor = new ThreadPoolExecutor(2, 5,
5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 20000; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "处理业务............");
});
}
System.out.println("任务执行完了..........");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
executor.shutdown();
}
}
}
我们创建了20000个任务:

DiscardPolicy策略
java
package com.qcby.pool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 玩线程池
* 模拟银行办理业务场景
*/
public class Demo2 {
public static void main(String[] args) {
ThreadPoolExecutor executor = null;
try {
//创建了一个线程池 核心线程2 最大线程5 阻塞队列容量1 空闲线程存活5s 默认的线程工厂和拒绝策略
executor = new ThreadPoolExecutor(2, 5,
5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy());
AtomicInteger res = new AtomicInteger();
for (int i = 0; i < 8; i++) {
executor.execute(() -> {
synchronized (res) {
res.getAndIncrement();
System.out.println(Thread.currentThread().getName() + "处理业务............" + res);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
executor.shutdown();
}
}
}
正常情况:

当我们触发静默丢弃策略时:

得到的结果是相同的,它把剩下的两个任务丢弃了

我们换成CallerRunsPolicy之后:

DiscardOldestPolicy
DiscardOldestPolicy会丢弃最先进入阻塞队列的任务
java
package com.qcby.pool;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 玩线程池
* 模拟银行办理业务场景
*/
public class Demo2 {
public static void main(String[] args) {
ThreadPoolExecutor executor = null;
try {
//创建了一个线程池 核心线程2 最大线程5 阻塞队列容量1 空闲线程存活5s 默认的线程工厂和拒绝策略
executor = new ThreadPoolExecutor(2, 5,
5, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 100; i++) {
Object finalI = i;
executor.execute(() -> {
synchronized (finalI) {
System.out.println(Thread.currentThread().getName() + "处理业务............" + finalI);
}
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
executor.shutdown();
}
}
}
结果:
pool-1-thread-1处理业务............0
pool-1-thread-2处理业务............1
pool-1-thread-5处理业务............7
pool-1-thread-3处理业务............5
pool-1-thread-2处理业务............53
pool-1-thread-1处理业务............47
pool-1-thread-4处理业务............6
pool-1-thread-2处理业务............72
pool-1-thread-3处理业务............71
pool-1-thread-5处理业务............55
pool-1-thread-3处理业务............79
pool-1-thread-5处理业务............81
pool-1-thread-1处理业务............80
pool-1-thread-3处理业务............85
pool-1-thread-4处理业务............83
pool-1-thread-5处理业务............82
pool-1-thread-1处理业务............86
pool-1-thread-3处理业务............87
pool-1-thread-4处理业务............89
pool-1-thread-1处理业务............88
pool-1-thread-2处理业务............90
pool-1-thread-1处理业务............91
pool-1-thread-5处理业务............93
pool-1-thread-1处理业务............94
pool-1-thread-5处理业务............95
pool-1-thread-4处理业务............96
pool-1-thread-1处理业务............97
pool-1-thread-5处理业务............98
pool-1-thread-3处理业务............99
后面92个任务不断触发拒绝策略,挤掉了队列中的老任务 最后执行的 99、98、97 等,是最后被保留的新任务 拒绝任务时静默丢弃,不会抛出异常。
最大线程数量怎么定?
1.CPU密集型,服务器有几核就用几个线程,在线程池构造器里面传入:
Runtime.getRuntime().availableProcessors()
获取CPU核数
2.IO密集型,判断有哪些线程执行时很经常进行IO操作的,最大线程数量大于其即可