<strike id="j6ksu"></strike>
<th id="j6ksu"></th>
  1. <th id="j6ksu"></th>

    Java创建线程池的几种方式具体实现

    Java基础 潘老师 1个月前 (10-13) 85 ℃ (0) 扫码查看

    在讲解Java创建线程池的几种方式之前,潘老师先带大家了解一些关于线程池的概念和原理。

    一、Java线程池是什么

    线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务,并且线程池中的线程都是后台线程。

    二、Java线程池作用意义

    第1:降低资源消耗。通过重复利用机制已降低线程创建和销毁造成的消耗。
    第2:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第3:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

    三、Java四种线程池的使用

    JDK1.5中引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。Java通过Executors为我们提供了四种线程池的创建方式,分别为:
    1)newCachedThreadPool
    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

    // 可缓存线程池 Executors表示启动线程的  可创建线程数是无限大小的
     ExecutorService executorService = Executors.newCachedThreadPool();
     for (int i = 0; i < 10; i++) {
         final int temp = i;
         // 可执行线程  execute 启动线程
         executorService.execute(new Runnable() {
             public void run() {
                 System.out.println(Thread.currentThread().getName() + "," + temp);
             }
         });
     }
     //停止线程池
     executorService.shutdown();
    

    2)newFixedThreadPool
    创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    //创建可固定长度的线程池,只会创建5个线程池进行处理
     ExecutorService executorService = Executors.newFixedThreadPool(5);
     for (int i = 0; i < 20; i++) {
         final int temp = i;
         // 可执行线程  execute 启动线程
         executorService.execute(new Runnable() {
             public void run() {
                 System.out.println(Thread.currentThread().getName() + "," + temp);
             }
         });
     }
     //停止线程池
     executorService.shutdown();
    

    3)newScheduledThreadPool

    创建一个定长线程池,支持定时及周期性任务执行。
    //创建可定时执行的线程池 数量为3
     ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
     for (int i = 0; i < 10; i++) {
         final int temp=i;
         //schedule 方法表示线程执行  表示延迟5秒之后 开始执行线程
         scheduledExecutorService.schedule(new Runnable() {
             public void run() {
                 System.out.println(Thread.currentThread().getName()+""+temp);
             }
         }, 5, TimeUnit.SECONDS);
     }
     //停止线程池
     scheduledExecutorService.shutdown();
    

    4)newSingleThreadExecutor
    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

    //创建单线程线程池
     ExecutorService executorService = Executors.newSingleThreadExecutor();
     for (int i = 0; i < 10; i++) {
         final int temp = i;
         // 可执行线程  execute 启动线程
         executorService.execute(new Runnable() {
             public void run() {
                 System.out.println(Thread.currentThread().getName() + "," + temp);
             }
         });
     }
     //停止线程池
     executorService.shutdown();
    

    查看Executors类源码,以newSingleThreadExecutor 方法为例:
    Java创建线程池的几种方式具体实现
    我们发现其实都是在使用ThreadPoolExecutor类进行构建的,然后该类目构造方法有很多参数。这些参数很重要,需要掌握,面试也会经常被问到!

    四、ThreadPoolExecutor线程池类7大参数详解

    参数 含义
    corePoolSize 核心线程数量,线程池维护线程的最少数量
    maximumPoolSize 线程池维护线程的最大数量
    keepAliveTime 线程池除核心线程外的其他线程的最长空闲时间,超过该时间的空闲线程会被销毁
    unit keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
    workQueue 线程池所使用的任务缓冲队列
    threadFactory 线程工厂,用于创建线程,一般用默认的即可
    rejectHandler 线程池拒绝任务时的处理策略

    五、Java线程池执行流程

    一个线程提交到线程池后的具体处理流程如下图所示:
    Java创建线程池的几种方式具体实现
    1)如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。

    2)如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。

    3)如果此时线程池中的数量大于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

    4)如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

    5)当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

    总结:处理任务判断的优先级为 核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler采取任务拒绝策略进行处理;
    线程池的拒绝策略:当最大线程数 + 队列缓存数量 小于线程数量的时候,程序运行出错,被拒绝。(在此不再详细展开)

    六、Java自定义线程池

    虽然JDK的Executors提供了4大类线程池供我们直接使用,但是在实际生产开发中,往往是不允许/不建议使用这4种直接调用的方式的,比如在阿里巴巴的Java开发手册中就有如下【强制】要求:
    Java创建线程池的几种方式具体实现
    原因就是因为直接创建会容易导致OOM(内存溢出~)
    推荐我们手工自定义线程池,在自定义时我们需要预估线程数多少合适,主要参考依据为核心线程数计算公式:

    • IO密集型:核心线程数 = CPU核数 / (1-阻塞系数)
    • CPU密集型:核心线程数 = CPU核数 + 1
    • IO密集型:核心线程数 = CPU核数 * 2

    可以创建个工具类如下:

    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class ThreadPoolUtil {
        /**
         * 默认 CPU 核心数
         */
        private static int threadPoolSize = 0;
    
        static {
            // 获取服务器 CPU 核心数
            threadPoolSize = Runtime.getRuntime().availableProcessors();
            System.out.println(" CPU 核心数量:" + threadPoolSize);
        }
    
        public static int getThreadPoolSize() {
            return threadPoolSize;
        }
    
        /**
         * 线程工厂,用来创建线程
         */
        private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-demo-%d").build();
    
        /**
         * 设置线程池核心参数(IO 密集型) 核心线程数 = CPU 核数 * 2
         * 参数依次分别为上面的的7大参数,最后拒绝策略参数可以不写,则默认使用AbortPolicy
         */
        private static ThreadPoolExecutor threadPoolExecutorIO =
            new ThreadPoolExecutor(threadPoolSize, threadPoolSize * 2, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
    
        /**
         * 设置线程池核心参数(CPU 密集型) 核心线程数 = CPU 核数 + 1 //  核心线程数 = CPU 核数 + 1
         */
        private static ThreadPoolExecutor threadPoolExecutorCPU =
            new ThreadPoolExecutor(threadPoolSize, threadPoolSize + 1, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy());
    
        /**
         * 返回线程池对象
         *
         * @return
         */
        public static ThreadPoolExecutor getThreadPoolExecutorIO() {
            return threadPoolExecutorIO;
        }
        public static ThreadPoolExecutor getThreadPoolExecutorCPU() {
            return threadPoolExecutorCPU;
        }
    }
    

    如果你不想区分CPU密集型还是IO密集型,只是想简单的自己手工创建一个线程池,比如我这里只想创建一个SingleThreadExecutor,则代码如下:

    ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1024), new ThreadFactoryBuilder().setNameFormat("thread-pool-demo-%d").build());
    for (int i = 0; i < 10; i++) {
         final int temp = i;
         // 可执行线程  execute 启动线程
         executorService.execute(new Runnable() {
             public void run() {
                 System.out.println(Thread.currentThread().getName() + "," + temp);
             }
         });
     }
    

    以上就是Java创建线程池的几种方式具体实现全部内容,如果你有什么疑问,可以评论留言~


    版权声明:本站所有文章,如无特殊说明,均为本站原创。全部下载资源版权归原作者所有。任何个人或组织,若未征得本站同意,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。若需转载请注明文章来源。
    本文链接:Java创建线程池的几种方式具体实现
    喜欢 (2)
    请潘老师喝杯Coffee吧!】
    分享 (0)
    用户头像
    发表我的评论
    取消评论
    表情 贴图 签到 代码

    Hi,您需要填写昵称和邮箱!

    • 昵称【必填】
    • 邮箱【必填】
    • 网址【可选】

    您也可以 微信登录 来发表评论!

    韩国毛茸茸的丰满妇女