• Java中线程池的创建与使用


    前言:默认线程池的弊端

    在线程池应用中,参考阿里巴巴java开发规范:线程池不允许使用Executors去创建,不允许使用系统默认的线程池,推荐通过ThreadPoolExecutor的方式,这样的处理方式让开发的工程师更加明确>线程池的运行规则,规避资源耗尽的风险。Executors各个方法的弊端:
    newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM
    newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。

    线程池中提交线程的方法,一种是execute()另外一种是submit()

    1:2种方法接收的参数不同
    2:submit()有返回值,execute()没有
    3:submit()方法便于Exception处理
    4:execute():提交不需要返回值的任务,无法判断任务是否被执行成功了
    5:submit():提交需要放回值的任务;线程会返回Future对象,通过Future的isDone()方法可以判断任务是否执行成功,并且可以通过Future.get()获取返回的值,get方法会阻塞,直到线程的完成
    而get(long timeout, TimeUnit unit)会在等待一段时间后返回,这段时间内任务可能没有执行完成

    线程池的创建可以分为以下两类

    1:通过 ThreadPoolExecutor 手动创建线程池
    2:通过 Executors 执行器自动创建线程池

    1:FixedThreadPool

    创建一个固定大小的线程池,可控制并发线程数。
    使用 FixedThreadPool 创建 2 个固定大小的线程池

    public static void fixedThreadPool() {
        // 创建 2 个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
     
        // 创建任务
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            }
        };
        threadPool.execute(runnable); 
        threadPool.execute(runnable); 
        threadPool.execute(runnable);
        threadPool.execute(runnable);
    }
    
    
    //如果觉得以上方法比较繁琐,还用使用以下简单的方式来实现线程池的创建和使用:
    public static void fixedThreadPool() {
        // 创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        // 执行任务
        threadPool.execute(() -> {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2:CachedThreadPool

    创建一个可缓存的线程池,若线程数超过任务所需,那么多余的线程会被缓存一段时间后才被回收,若线程数不够,则会新建线程。有多少任务就动态创建多少线程,线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。CachedThreadPool 是根据短时间的任务量来决定创建的线程数量的,所以它适合短时间内有突发大量任务的处理场景

    public 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(1);
                } catch (InterruptedException e) {
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3:SingleThreadExecutor

    创建单个线程的线程池,它可以保证线程先进先出的执行顺序
    单个线程的线程池相比于线程来说,它的优点有以下 2 个:
    1:可以复用线程:即使是单个线程池,也可以复用线程。
    2:提供了任务管理功能:单个线程池也拥有任务队列,在任务队列可以存储多个任务,这是线程无法实现的,并且当任务队列满了之后,可以执行拒绝策略,这些都是线程不具备的。

    public 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(1);
                } catch (InterruptedException e) {
                }
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    4:ScheduledThreadPool

    创建一个可以执行延迟任务的线程池
    执行结果是,任务在 2 秒之后被执行了,实现了延迟 1s 再执行任务

    public static void scheduledThreadPool() {
        // 创建线程池
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
        // 添加定时执行任务(2s 后执行)
        System.out.println("添加任务,时间:" + new Date());
        threadPool.schedule(() -> {
            System.out.println("任务被执行,时间:" + new Date());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        }, 2, TimeUnit.SECONDS);
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5:ThreadPoolExecutor

    ThreadPoolExecutor 是最原始、也是最推荐的手动创建线程池的方式,它在创建时最多提供 7 个参数可供设置

    public static void myThreadPoolExecutor() {
        // 创建线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
        5, //corePoolSize 核心线程数
        10, //MaxPS  最大线程数
        100, //keepAliveTime  存活时间
        TimeUnit.SECONDS, //TimeUnit  时间单位
        new LinkedBlockingQueue<>(10),//BlockingQueue  任务队列
        Executors.defaultThreadFactory(),//ThreadFactory  线程工厂
        new ThreadPoolExecutor.DiscardPolicy());//RejectStrategy  拒绝策略
    
        // 执行任务
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • corePoolSize:线程池核心线程数(平时保留的线程数)
    • maximumPoolSize:线程池最大线程数(当workQueue都放不下时,启动新线程,最大线程数)
    • keepAliveTime:超出corePoolSize数量的线程的保留时间。
    • unit:keepAliveTime单位
    • workQueue:阻塞队列,存放来不及执行的线程
      ArrayBlockingQueue:构造函数一定要传大小
      LinkedBlockingQueue:构造函数不传大小会默认为(Integer.MAX_VALUE ),当大量请求任务时,容易造成 内存耗尽。
      SynchronousQueue:同步队列,一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程。
      PriorityBlockingQueue : 优先队列
    • threadFactory:线程工厂
    • handler:饱和策略,当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略
      AbortPolicy(默认):该策略下,直接丢弃任务,并抛出RejectedExecutionException异常
      CallerRunsPolicy:用调用者的线程执行任务,该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务
      DiscardOldestPolicy:该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
      DiscardPolicy:该策略下,直接丢弃任务,什么都不做
  • 相关阅读:
    Jmeter初始学习
    【数据结构】堆的实现(简单易懂,超级详细!!!)
    聚焦算力技术发展,中科驭数成功举办“新一代智算中心”技术生态沙龙
    HttpClient远程使用大全
    【JavaWeb】之文件上传与下载
    做了五年功能测试麻木了,现在想进阶自动化测试该从哪里开始?
    Google Earth Engine APP——在线计算23类植被指数app代码
    第6章 字符串操作
    cesium01-vue中创建一个三维地球和相关的控件属性注释
    解决“无法载入 mcrypt 扩展”问题
  • 原文地址:https://blog.csdn.net/qq_19891197/article/details/127424200