zoukankan      html  css  js  c++  java
  • 线程池

    继承Thread的弊端

    1.每次new Thread的时候都需要新建一个线程,性能差

    2.线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM

    3.Thread类缺少更多功能,比如更多的执行、定期执行、线程中断。

    线程池的好处

    1.重用存在的线程,减少对象创建、消亡的开销、性能佳

    2.可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞。

    3.提供定时执行、定期执行、单线程、并发数控制等功能。

    线程池-ThreadPoolExecutor

    参数说明:ThreadPoolExecutor一共有七个参数,这七个参数配合起来,构成了线程池强大的功能。

    corePoolSize:核心线程数线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动,除非调用了prestartCoreThread/prestartAllCoreThreads事先启动核心线程。

    maximumPoolSize:线程最大线程数,线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。

    workQueue : 阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响

    ThreadPoolExecutorexecute方法分下面4种情况。
    1)如果当前运行的线程少于corePoolSize则创建新线程来行任(注意,一步骤需要取全局)。
    2)如果运行的线程等于或多于corePoolSize将任加入BlockingQueue
    3)如果无法将任加入BlockingQueue列已),则创建新的线程来理任(注意,执行一步需要取全局)。
    4)如果建新线程将使当前运行的线程超出maximumPoolSize,任将被拒,并用RejectedExecutionHandler.rejectedExecution()方法。

    线程池大小调节策略。通过corePoolSize和maximumPoolSize,控制如何新增线程;通过allowCoreThreadTimeOut和keepAliveTime,控制如何销毁线程。

    allowCoreThreadTimeOut:

    该属性用来控制是否允许核心线程超时退出。如果线程池的大小已经达到了corePoolSize,不管有没有任务需要执行,线程池都会保证这些核心线程处于存活状态。可以知道:该属性只是用来控制核心线程的。

    keepAliveTime:

    线程超时退出时间。举个例子,如果线程池的核心大小corePoolSize=5,而当前大小poolSize =8,那么超出核心大小的线程,会按照keepAliveTime的值判断是否会超时退出。如果线程池的核心大小corePoolSize=5,而当前大小poolSize =5,那么线程池中所有线程都是核心线程,这个时候线程是否会退出,取决于allowCoreThreadTimeOut。

    unit : keepAliveTime的时间单位

    threadFactory:线程工厂,用来创建线程

    rejectHandler:当拒绝处理任务时的策略

    四种拒绝策略:

    AbortPolicy :直接抛出异常

    CallerRunsPolicy   :用调用者所在的线程来执行任务

    DiscardOldestPolicy :线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。 

    DiscardPolicy    :不处理,直接丢弃

    • running:能接受新提交的任务,也能处理阻塞队列中的任务
    • shutdown:不能处理新的任务,但是能继续处理阻塞队列中任务
    • stop:不能接收新的任务,也不处理队列中的任务
    • tidying:如果所有的任务都已经终止了,这时有效线程数为0
    • terminated:最终状态
    序号方法名描述
    1 execute() 提交任务,交给线程池执行
    2 submit() 提交任务,能够返回执行结果 execute+Future
    3 shutdown() 关闭线程池,等待任务都执行完
    4 shutdownNow() 关闭线程池,不等待任务执行完
    5 getTaskCount() 线程池已执行和未执行的任务总数
    6 getCompleteTaskCount() 已完成的任务数量
    7 getPoolSize() 线程池当前的线程数量
    8 getActiveCount()

    当前线程池中正在执行任务的线程数量

    使用Executor创建线程池

    使用Executor可以创建四种线程池:分别对应上边提到的四种线程池初始化方法

    1、Executors.newCachedThreadPool 

    • 它是一个可以无限扩大的线程池;
    • 它比较适合处理执行时间比较小的任务;
    • corePoolSize为0,maximumPoolSize为无限大,意味着线程数量可以无限大;
    • keepAliveTime为60S,意味着线程空闲时间超过60S就会被杀死;
    • 采用SynchronousQueue装等待的任务,这个阻塞队列没有存储空间,这意味着只要有请求到来,就必须要找到一条工作线程处理他,如果当前没有空闲的线程,那么就会再创建一条新的线程。
    //使用方法:
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}", index);
                }
            });
        }
        executorService.shutdown();
    }

    值得注意的一点是,newCachedThreadPool的返回值是ExecutorService类型,该类型只包含基础的线程池方法,但却不包含线程监控相关方法,因此在使用返回值为ExecutorService的线程池类型创建新线程时要考虑到具体情况。 

    2、newFixedThreadPool 
    定长线程池,可以线程现成的最大并发数,超出在队列等待

    • 它是一种固定大小的线程池;
    • corePoolSize和maximunPoolSize都为用户设定的线程数量nThreads;
    • keepAliveTime为0,意味着一旦有多余的空闲线程,就会被立即停止掉;但这里keepAliveTime无效;
    • 阻塞队列采用了LinkedBlockingQueue,它是一个无界队列;
    • 由于阻塞队列是一个无界队列,因此永远不可能拒绝任务;
    • 由于采用了无界队列,实际线程数量将永远维持在nThreads,因此maximumPoolSize和keepAliveTime将无效。
    //使用方法:
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}", index);
                }
            });
        }
        executorService.shutdown();
    }

    3、newSingleThreadExecutor 
    单线程化的线程池,用唯一的一个共用线程执行任务,保证所有任务按指定顺序执行(FIFO、优先级…)

    • 它只会创建一条工作线程处理任务;
    • 采用的阻塞队列为LinkedBlockingQueue;
    //使用方法:
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("task:{}", index);
                }
            });
        }
        executorService.shutdown();
    }

    4、newScheduledThreadPool 
    定时线程池,支持定时和周期任务执行

    //基础使用方法:
    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                log.warn("schedule run");
            }
        }, 3, TimeUnit.SECONDS);//延迟3秒执行
        executorService.shutdown();
    }

    小扩展:延迟执行任务的操作,java中还有Timer类同样可以实现

    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            log.warn("timer run");
        }
    }, new Date(), 5 * 1000);
  • 相关阅读:
    Linux 命令汇总总结相关
    数据结构---python---表
    python-----Queue模块
    再看python多线程------threading模块
    <转> Struct 和 Union区别 以及 对内存对齐方式的说明
    python装饰器
    HTTP权威指南----缓存
    HTTP权威指南----连接管理
    以python理解Linux的IO多路复用,select、poll、epoll
    <转载> pycharm快捷键及一些常用设置
  • 原文地址:https://www.cnblogs.com/xiangkejin/p/9275488.html
Copyright © 2011-2022 走看看