线程池的工作原理
线程池可以减少创建和销毁线程的次数,从而减少系统资源的消耗,当一个任务提交到线程池时
a. 首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线程执行任务,否则进入下一步
b. 判断工作队列是否已满,没有满则加入工作队列,否则执行下一步
c. 判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务,否则执行饱和策略,默认抛出异常
线程池的创建⽅法总共有 7 种,但总体来说可分为 2 类:
通过 Executors 创建(6种)
通过 ThreadPoolExecutor 创建
方式 | 作用 |
---|---|
Executors.newFixedThreadPool | 创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待 |
Executors.newCachedThreadPool | 创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程 |
Executors.newSingleThreadExecutor | 创建单个线程数的线程池,它可以保证先进先出的执⾏顺序 |
Executors.newScheduledThreadPool | 创建⼀个可以执⾏延迟任务的线程池 |
Executors.newSingleThreadScheduledExecutor | 创建⼀个单线程的可以执⾏延迟任务的线程池 |
Executors.newWorkStealingPool | 创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】 |
ThreadPoolExecutor | 最原始的创建线程池的⽅式,它包含了 7 个参数可供设置 |
private static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);//创建的线程数
System.out.println(Thread.currentThread().getName() + " , thread id: " + Thread.currentThread().getId());
fixedThreadPool.submit(() -> System.out.println(Thread.currentThread().getName() + " , thread id: " + Thread.currentThread().getId()));
fixedThreadPool.submit(() -> System.out.println(Thread.currentThread().getName() + " , thread id: " + Thread.currentThread().getId()));
// 注释1
// fixedThreadPool.submit(() -> System.out.println(Thread.currentThread().getName() + " , thread id: " + Thread.currentThread().getId()));
}
执行结果
main , thread id: 1
pool-1-thread-1 , thread id: 22
pool-1-thread-2 , thread id: 23
当注释1处放开执行该代码时,打印的结果是:
main , thread id: 1
pool-1-thread-1 , thread id: 22
pool-1-thread-2 , thread id: 23
pool-1-thread-1 , thread id: 22
返回结果
**使用submit可以执行有返回值的任务或者是无返回值的任务;而execute只能执行不带返回值的任务。**
Future < T > submit(Callable < T > task);
Future < T > submit(Runnable task, T result);
Future> submit (Runnable task);
private static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);//创建的线程数
Future<Integer> submit = fixedThreadPool.submit(() -> {
int nextInt = new Random().nextInt(30);
System.out.println("nextInt:" + nextInt);
return nextInt;
});
try {
System.out.println("submit:" + submit.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
输出结果
nextInt:25
submit:25
private static void main(String[] args) {
// 创建线程工厂
ThreadFactory threadFactory = r -> {
//注意:要把任务Runnable设置给新创建的线程
Thread thread = new Thread(r);
//设置线程的命名规则
thread.setName("张三的线程:" + r.hashCode());
//设置线程的优先级
thread.setPriority(Thread.MAX_PRIORITY);
return thread;
};
ExecutorService threadPool = Executors.newFixedThreadPool(2, threadFactory);
//执行任务1
Future<Integer> result = threadPool.submit(() -> {
int num = new Random().nextInt(10);
System.out.println(Thread.currentThread().getPriority() + ", name: " + Thread.currentThread().getName() + ", 随机数:" + num);
return num;
});
//打印线程池返回结果
try {
System.out.println("返回结果:" + result.get());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
输出结果:
10, name: 张三的线程:682376643, 随机数:3
返回结果:3
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 15; i++) {
int finalI = i;
executorService.submit(() -> {
System.out.println("i : " + finalI + ", 线程名称:" + Thread.currentThread().getName());
});
}
}
输出结果:
i : 0, 线程名称:pool-1-thread-1
i : 7, 线程名称:pool-1-thread-8
i : 6, 线程名称:pool-1-thread-7
i : 5, 线程名称:pool-1-thread-6
i : 4, 线程名称:pool-1-thread-5
i : 2, 线程名称:pool-1-thread-3
i : 3, 线程名称:pool-1-thread-4
i : 1, 线程名称:pool-1-thread-2
i : 12, 线程名称:pool-1-thread-13
i : 11, 线程名称:pool-1-thread-12
i : 10, 线程名称:pool-1-thread-11
i : 9, 线程名称:pool-1-thread-10
i : 8, 线程名称:pool-1-thread-9
i : 14, 线程名称:pool-1-thread-15
i : 13, 线程名称:pool-1-thread-14
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
int finalI = i;
singleThreadExecutor.execute(() -> {
try {
Thread.sleep(1000);
System.out.println("i:" + finalI + " , time:" + LocalDateTime.now() + " , name:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
}
输出结果:
i:0 , time:2023-06-14T09:57:42.504660900 , name:pool-1-thread-1
i:1 , time:2023-06-14T09:57:43.513563300 , name:pool-1-thread-1
i:2 , time:2023-06-14T09:57:44.515762300 , name:pool-1-thread-1
i:3 , time:2023-06-14T09:57:45.524664800 , name:pool-1-thread-1
i:4 , time:2023-06-14T09:57:46.532360900 , name:pool-1-thread-1
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加的时间 :" + LocalDateTime.now());
//执行定时任务(延迟3s执行)只执行一次
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行子任务时间 :" + LocalDateTime.now());
}
}, 3, TimeUnit.SECONDS);
输出结果:
添加的时间 :2023-06-14T10:06:08.407704800
执行子任务时间 :2023-06-14T10:06:11.413525800
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
System.out.println("添加的时间 :" + LocalDateTime.now());
//2s之后开始执行定时任务,定时任务每隔2s执行一次
scheduledThreadPool.scheduleAtFixedRate(() -> System.out.println("执行子任务时间 :" + LocalDateTime.now()), 3, 2, TimeUnit.SECONDS);
try {
Thread.sleep(60000);
scheduledThreadPool.shutdown();//任务停止
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
输出结果:
添加的时间 :2023-06-14T10:14:10.654523300
执行子任务时间 :2023-06-14T10:14:13.667103
执行子任务时间 :2023-06-14T10:14:15.659165900
执行子任务时间 :2023-06-14T10:14:17.664069700
执行子任务时间 :2023-06-14T10:14:19.662755200
执行子任务时间 :2023-06-14T10:14:21.660791400
执行子任务时间 :2023-06-14T10:14:23.670349700
执行子任务时间 :2023-06-14T10:14:25.671597300
执行子任务时间 :2023-06-14T10:14:27.656823800
执行子任务时间 :2023-06-14T10:14:29.662409500
.......
scheduleAtFixedRate VS scheduleWithFixedDelay区别
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
System.out.println("添加任务的时间:" + LocalDateTime.now());
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行任务的时间:" + LocalDateTime.now()+" , 线程名:" + Thread.currentThread().getName());
}
},2, TimeUnit.SECONDS );
}
输出结果:
添加任务的时间:2023-06-14T10:25:25.332814
执行任务的时间:2023-06-14T10:25:27.347112700 , 线程名:pool-1-thread-1
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
System.out.println("添加任务的时间:" + LocalDateTime.now());
for (int i = 0; i < 5; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("执行任务的时间:" + LocalDateTime.now() + " , 线程名:" + Thread.currentThread().getName());
}
});
}
}
输出结果:
添加任务的时间:2023-06-14T10:28:27.596905300
执行任务的时间:2023-06-14T10:28:27.598899 , 线程名:pool-1-thread-1
执行任务的时间:2023-06-14T10:28:27.598899 , 线程名:pool-1-thread-1
执行任务的时间:2023-06-14T10:28:27.598899 , 线程名:pool-1-thread-1
执行任务的时间:2023-06-14T10:28:27.598899 , 线程名:pool-1-thread-1
执行任务的时间:2023-06-14T10:28:27.598899 , 线程名:pool-1-thread-1
public static void main(String[] args) {
ExecutorService newWorkStealingPool = Executors.newWorkStealingPool();
for (int i = 0; i < 10; i++) {
newWorkStealingPool.submit(() -> {
System.out.println("线程名:" + Thread.currentThread().getName());
});
//确保任务执行完成
while (!newWorkStealingPool.isTerminated()) {
}
}
}
输出结果:
线程名:ForkJoinPool-1-worker-19
corePoolSize 线程池核心线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。
maximumPoolSize 线程池最大线程数量
最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)
keepAliveTime 空闲线程存活时间
非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉 注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);
unit 空闲线程存活时间单位 keepAliveTime的计量单位
workQueue 工作队列
新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队:ArrayBlockingQueue、LinkedBlockingQuene、SynchronousQuene、PriorityBlockingQueue
threadFactory 线程工厂
创建一个新线程时使用的工厂,默认使用Executors.defaultThreadFactory(),也可以使用guava库的ThreadFactoryBuilder来创建,可以用来设定线程名、是否为daemon线程等等
handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略:CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy
源码:
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
public final class ThreadPoolManager {
//线程池中最少的线程的数量
private static final int CORE_POOL_SIZE = 8;
//线程池中最多的线程的数量
private static final int MAXIMUM_POOL_SIZE = 15;
//空闲线程的最长存活时间
private static final int KEEP_ALIVE = 10;
//工作队列-阻塞队列
private static final BlockingQueue<Runnable> mWorkQueue = new LinkedBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE);
//线程池中线程的创建工厂
private static final ThreadFactory mThreadFactory = new ThreadFactory() {
//原子操作级别的整数,初始值是1
private final AtomicInteger mCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
PcmRecorder.bandCpu();
Thread thread = new Thread(r, "Task #" + mCount.getAndIncrement()); //线程名,前缀固定,后缀自增长
thread.setPriority(Thread.NORM_PRIORITY - 1); //正常优先级
return thread;
}
};
//线程池中线程的执行的管理器
public static final ExecutorService EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE,
KEEP_ALIVE, TimeUnit.SECONDS,
mWorkQueue, mThreadFactory,
new ThreadPoolExecutor.DiscardPolicy());
}
调用
ThreadPoolManager.EXECUTOR.submit(()->{
//todo someting
//通过handler进行线程间通信
});
封装好的线程池链接:
妈妈再也不用担心你不会使用线程池了(ThreadUtils)