zoukankan      html  css  js  c++  java
  • 线程池的使用

    线程池的使用

      促使我去研究线程的使用场景,原因是因为每次编译器显示创建线程的时候,阿里的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。

    知人者智,自知者明,胜人者有力,自胜者强。
  • 相关阅读:
    NSIS附加数据库,分离数据库脚本代码
    C# 昨天今天明天上周本周下周上月本月下月等日期计算
    NSIS安装MSDE2000和NET2.0脚本代码
    sql 获取指定数据表的所有字段名称的字符串
    如何检测TerraGate的InternetLicense运行是否正常
    Skyline TEP5.1.3二次开发入门——初级(七)
    Skyline软件二次开发初级——2如何在WEB页面中控制三维地图的观察点坐标和角度
    如何实现Skyline与微软bing地图的联动
    Skyline软件二次开发初级——3如何在WEB页面中的三维地图上创建几何对象
    Skyline软件二次开发初级——1如何在web页面中添加控件和加载三维地图数据
  • 原文地址:https://www.cnblogs.com/nanfengxiangbei/p/14189604.html
Copyright © 2011-2022 走看看