线程池的使用

线程池的使用

 

  促使我去研究线程的使用场景,原因是因为每次编译器显示创建线程的时候,阿里的java规范都会抛出一个警告,而我每次看到都会比较难受,哈哈哈,大佬都说不让用,那我们就看看使用线程池会有什么好处。

线程池的优势主要体现在以下 4 点:

1、降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
2、提高响应速度:任务到达时,无需等待线程创建即可立即执行。
3、提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。
使用线程池可以进行统一的分配、调优和监控。
4、提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

 

import java.util.Date;
import java.util.concurrent.*;

/**
* 线程池的使用
*
* @author nanfengxiangbei
* @date 2020/11/24 11:20
*/
public class ThreadPoolTest {

/**
* 创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
*/
private static void fixedThreadPool() {
// 创建 2 个数据级的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);

// 执行任务
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> System.out.println("任务被执行,线程:" + Thread.currentThread().getName()));
}
}

/**
* 创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
*/
private static void cachedThreadPool() {
// 创建线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// 执行任务
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}

/**
* 创建单个线程数的线程池,它可以保证先进先出的执行顺序。
*/
private static void singleThreadExecutor() {
// 创建线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + ":任务被执行");
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}

/**
* 创建一个可以执行延迟任务的线程池。
*/
private static void scheduledThreadPool() {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
// 添加定时执行任务(1s 后执行)
System.out.println("添加任务,时间:" + DateUtil.getDateStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + DateUtil.getDateStr(new Date(), "yyyy-MM-dd HH:mm:ss"));
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 5, TimeUnit.SECONDS);
}

/**
* 创建一个单线程的可以执行延迟任务的线程池。
*/
private static void singleThreadScheduledExecutor() {
// 创建线程池
ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
// 添加定时执行任务(2s 后执行)
System.out.println("添加任务,时间:" + new Date());
threadPool.schedule(() -> {
System.out.println("任务被执行,时间:" + new Date());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 2, TimeUnit.SECONDS);
}

/**
* 创建一个抢占式执行的线程池(任务执行顺序不确定),注意此方法只有在 JDK 1.8+ 版本中才能使用。
*/
private static void workStealingPool() {
// 创建线程池
ExecutorService threadPool = Executors.newWorkStealingPool();
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
});
}
// 确保任务执行完成
while (!threadPool.isTerminated()) {
System.out.println("开始了");
}
}

/**
* 常采用此方法创建
* 最原始的创建线程池的方式,它包含了 7 个参数可供设置。
*/
private static void myThreadPoolExecutor() {
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
// 执行任务
for (int i = 0; i < 10; i++) {
final int index = i;
threadPool.execute(() -> {
System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}

public static void main(String[] args) {
//fixedThreadPool();
//cachedThreadPool();
//singleThreadExecutor();
//scheduledThreadPool();
//singleThreadScheduledExecutor();
//workStealingPool();
myThreadPoolExecutor();
}
}

 

总结:推荐使用myThreadPoolExecutor,Executors 创建会带来下面的弊端,弄不好还OOM

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

1) FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

posted @ 2020-12-25 16:33  南风向北  阅读(95)  评论(0编辑  收藏  举报