线程池的创建⽅法总共有 7 种,但总体来说可分为 2 类:
-
通过 ThreadPoolExecutor
创建的线程池; -
通过 Executors
创建的线程池。
线程池的创建⽅式总共包含以下 7 种(其中 6 种是通过 Executors
创建的, 1 种是通过ThreadPoolExecutor
创建的):
-
Executors.newFixedThreadPool
:创建⼀个固定⼤⼩的线程池,可控制并发的线程数,超出的线程会在队列中等待; -
Executors.newCachedThreadPool
:创建⼀个可缓存的线程池,若线程数超过处理所需,缓存⼀段时间后会回收,若线程数不够,则新建线程; -
Executors.newSingleThreadExecutor
:创建单个线程数的线程池,它可以保证先进先出的执⾏顺序; -
Executors.newScheduledThreadPool
:创建⼀个可以执⾏延迟任务的线程池; -
Executors.newSingleThreadScheduledExecutor
:创建⼀个单线程的可以执⾏延迟任务的线程池; -
Executors.newWorkStealingPool
:创建⼀个抢占式执⾏的线程池(任务执⾏顺序不确定)【JDK1.8 添加】。 -
ThreadPoolExecutor
:最原始的创建线程池的⽅式,它包含了 7 个参数可供设置,后⾯会详细讲。
1. 固定数量的线程池
public class ThreadPoolDemo3 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//添加任务方式 1
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
//添加任务方式2
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
输出:
pool-1-thread-1
pool-1-thread-2
a. 线程池返回结果
public class ThreadPoolDemo4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService threadPool = Executors.newFixedThreadPool(2);
//执行任务
Future<Integer> result = threadPool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(10);
System.out.println("随机数" + num);
return num;
}
});
//打印线程池返回方式
System.out.println("返回结果:" + result.get());
}
}
输出
随机数8
返回结果:8
使用submit可以执行有返回值的任务或者是无返回值的任务;而execute只能执行不带返回值的任务。
b. ⾃定义线程池名称或优先级
public class ThreadPoolDemo5 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
@Override
public Thread newThread(Runnable 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(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int num = new Random().nextInt(10);
System.out.println(Thread.currentThread().getPriority() + ", 随机数:" + num);
return num;
}
});
//打印线程池返回结果
System.out.println("返回结果:" + result.get());
}
}
提供的功能:
-
设置(线程池中)线程的命名规则。
-
设置线程的优先级。
-
设置线程分组。
-
设置线程类型(用户线程、守护线程)。
2. 带缓存的线程池
public class ThreadPoolDemo6 {
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
int finalI = i;
service.submit(() -> {
System.out.println("i : " + finalI + "|线程名称:" + Thread.currentThread().getName());
});
}
}
}
输出
i : 1|线程名称:pool-1-thread-2
i : 4|线程名称:pool-1-thread-5
i : 3|线程名称:pool-1-thread-4
i : 5|线程名称:pool-1-thread-6
i : 0|线程名称:pool-1-thread-1
i : 2|线程名称:pool-1-thread-3
i : 6|线程名称:pool-1-thread-7
i : 7|线程名称:pool-1-thread-8
i : 8|线程名称:pool-1-thread-9
i : 9|线程名称:pool-1-thread-1
优点:线程池会根据任务数量创建线程池,并且在一定时间内可以重复使用这些线程,产生相应的线程池。
缺点:适用于短时间有大量任务的场景,它的缺点是可能会占用很多的资源。
3. 执⾏定时任务
a. 延迟执⾏(⼀次)
public class ThreadPoolDemo7 {
public static void main(String[] args) {
//创建线程池
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);
}
}
输出
添加任务的时间:2022-04-13T14:19:39.983
执行子任务:2022-04-13T14:19:42.987
b. 固定频率执⾏
public class ThreadPoolDemo8 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:" + LocalDateTime.now());
//2s之后开始执行定时任务,定时任务每隔4s执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" + LocalDateTime.now());
}
},2,4, TimeUnit.SECONDS);
}
}
输出
添加任务时间:2022-04-13T14:24:38.810
执行任务:2022-04-13T14:24:40.814
执行任务:2022-04-13T14:24:44.814
执行任务:2022-04-13T14:24:48.813
执行任务:2022-04-13T14:24:52.815
执行任务:2022-04-13T14:24:56.813
执行任务:2022-04-13T14:25:00.813
执行任务:2022-04-13T14:25:04.814
执行任务:2022-04-13T14:25:08.813
... ...
... ...
执行任务:2022-04-13T14:26:44.814
执行任务:2022-04-13T14:26:48.813
注意事项:
public class ThreadPoolDemo9 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:" + LocalDateTime.now());
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务: " + LocalDateTime.now());
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4, TimeUnit.SECONDS);
}
}
输出
添加任务时间:2022-04-13T14:33:34.551
执行任务: 2022-04-13T14:33:36.556
执行任务: 2022-04-13T14:33:41.557
执行任务: 2022-04-13T14:33:46.559
执行任务: 2022-04-13T14:33:51.561
执行任务: 2022-04-13T14:33:56.562
执行任务: 2022-04-13T14:34:01.564
执行任务: 2022-04-13T14:34:06.566
执行任务: 2022-04-13T14:34:11.566
执行任务: 2022-04-13T14:34:16.567
执行任务: 2022-04-13T14:34:21.570
执行任务: 2022-04-13T14:34:26.570
... ....
c. scheduleAtFixedRate
VS scheduleWithFixedDelay
scheduleAtFixedRate
是以上⼀次任务的开始时间,作为下次定时任务的参考时间的(参考时间+延迟任务=任务执⾏)。scheduleWithFixedDelay
是以上⼀次任务的结束时间,作为下次定时任务的参考时间的。
public class ThreadPoolDemo10 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:" + LocalDateTime.now());
//2s之后开始执行定时任务,定时任务每隔4s执行一次
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:" + LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 2, 4, TimeUnit.SECONDS);
}
}
输出
添加任务时间:2022-04-13T14:46:02.871
执行任务:2022-04-13T14:46:04.876
执行任务:2022-04-13T14:46:09.878
执行任务:2022-04-13T14:46:14.880
执行任务:2022-04-13T14:46:19.883
执行任务:2022-04-13T14:46:24.885
执行任务:2022-04-13T14:46:29.888
执行任务:2022-04-13T14:46:34.888
执行任务:2022-04-13T14:46:39.891
执行任务:2022-04-13T14:46:44.893
执行任务:2022-04-13T14:46:49.895
执行任务:2022-04-13T14:46:54.897
执行任务:2022-04-13T14:46:59.900
执行任务:2022-04-13T14:47:04.901
... ...
4. 定时任务单线程
public class ThreadPoolDemo11 {
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());
}
},2, TimeUnit.SECONDS );
}
}
输出
添加任务的时间:2022-04-13T15:06:38.100
执行时间:2022-04-13T15:06:40.106
5. 单线程线程池
public class ThreadPoolDemo12 {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadScheduledExecutor();
for (int i = 0; i < 10; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程名:" + Thread.currentThread().getName());
}
});
}
}
}
输出
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
线程名:pool-1-thread-1
(MS) 为什么不直接用线程?
单线程的线程池又什么意义?
-
复用线程。
-
单线程的线程池提供了任务队列和拒绝策略(当任务队列满了之后(Integer.MAX_VALUE),新来的任务就会拒绝策略)
6. 根据当前CPU⽣成线程池
public class ThreadPoolDemo13 {
public static void main(String[] args) {
ExecutorService service = Executors.newWorkStealingPool();
for (int i = 0; i < 10; i++) {
service.submit(() -> {
System.out.println("线程名" + Thread.currentThread().getName());
});
while(!service.isTerminated()) {
}
}
}
}
输出
线程名ForkJoinPool-1-worker-1
7. ThreadPoolExecutor
参考:https://blog.csdn.net/m0_48273471/article/details/124171220
感谢阅读,希望对你有所帮助 :)
来源:blog.csdn.net/m0_48273471/article/
details/124145012
精彩推荐
原文始发于微信公众号(Java面试题精选):【433期】面试官:线程池的7种创建方式,你都清楚吗?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/126643.html