zoukankan      html  css  js  c++  java
  • 并发编程

    并发编程系列

    相比于线程池,我们可能接触new Thread更多一点,既然有了new Thread我们为什么还要使用线程池呢?

    new Thread的弊端
    a、每次new Thread新建对象,性能差

    b、线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM(OutOfMemory)
    
    c、缺少更多功能,如更多执行、定期执行、线程中断
    

    线程池的优势
    a、重用存在的线程,减少对象创建、消亡的开销,性能好

     b、可有效控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
    
     c、提供定时执行、定期执行、单线程、并发数控制等功能
    

    线程池类图

    由类图可以看出,ThreadPoolExecutor类是线程池中最核心的一个类。我们来做重点分析

    ThreadPoolExecutor构造参数
    a、corePoolSize:核心线程数量,默认情况下(可预创建线程)线程池后线程池中的线程数为0,当有任务后当有任务后就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列中

     b、maximumPoolSize:线程最大线程数
    
     c、workQueue:阻塞队列,存储等待执行的任务,有三种取值,ArrayBlockQueue(基于数组的先进先出队列,创建时必须指定大小)、LinkedBlockingQueue(基于链表的先进先出队列,如果没有指定此队列大小,默认为Integer.MAX_VALUE)、SynchronousQueue(不会保存提交的任务,直接新建一个线程来执行新的任务)
    
     d、keepAliveTime:线程没有任务执行时最多保持多久时间终止,默认情况只有当线程池中的线程数大于corePoolSize时,keepAliveTIme才会起作用,当线程池中的线程数大于corePoolSize,如果一个线程的空闲时间达到keepAliveTime,则会终止。如果调用allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时keepAliveTime参数也会起作用,直到线程池的线程数为0
    
     e、unit:keepAliveTime的时间单位,有7中取值,如:TimeUnit.DAYS; 天,可具体到纳秒
    
     f、threadFactory:线程工厂,用来创建线程
    
     g、rejectHandler:当拒绝处理任务时的策略,通常有四种取值,ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;ThreadPoolExecutor.DiscardPolicy:丢弃任务,但不抛出异常;ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,重新尝试执行任务(重复此过程);ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
    

    参数之间的关系如下:
    a、如果当前poolsize小于corePoolSize,创建新线程执行任务

    b、如果当前poolsize大于corePoolsize,且等待队列未满,进入等待队列

    c、如果当前poolsize大于corePoolsize且小于maximumPoolSize,且等待队列已满,创建新线程执行任务

    d、如果当前poolsize大于corePoolSize且大于maximumPoolSize,且等待队列已满则用拒绝策略来处理该任务

    e、线程池中的线程执行完任务后不会立刻退出,而是去检查等待队列是否有新的线程去执行,如果在keepAliveTime里等不到新任务,线程就会退出

    ThreadPoolExecutor状态

    图中需要注意的是shutdown()和shutdownNow()方法的区别,执行前者后,还在执行的线程会执行完再关闭,执行后者后,线程池会立刻关闭,正在执行的线程不再执行

    ThreadPoolExecutor方法
    a、execute():提交任务,交给线程池执行

    b、submit():提交任务,能够返回执行结果 execute+Future

    c、shutdown():关闭线程池,等待任务都执行完

    d、shutdownNow():关闭线程池,不等待任务执行完

    e、getTaskCount():线程池已执行的和未执行的任务总数

    f、getCompletedTaskCount():已完成的任务数量

    g、getPoolSize():线程池当前线程数量

    h、getActiveCount():当前线程池正在执行任务的线程数量

    Executors类
    由上面的类图可以看出,Executors类为Executor,ExecutorService,ScheduledExecutorService提供了一些工具方法,并且Executors封装了ScheduledThreadPoolExecutor类和ThreadPoolExecutor类,所以我们倡导在实际使用中使用此类,Executors类提供了四个静态方法:

    a、newCachedThreadPool:创建可缓存线程池,灵活回收空闲线程,如果没有可回收线程,则创建新线程

    由上图可看出,newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用SynchronousQueue,就是说有新任务就创新线程运行,线程空闲超过60秒,则销毁线程。demo代码如下:

    复制代码
    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();
    }
    

    复制代码

    b、newFixedThreadPool:创建定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

    newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值相等,线程空闲后直接销毁,使用的是LinkedBlockingQueue。demo代码如下:

    复制代码
    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();
    }
    

    复制代码

    c、newScheduledThreadPool:创建大小无限制线程池(最大为Integer.MAX_VALUE),支持定时及周期性任务执行

    newScheduledThreadPool的特点是可以进行任务调度,最常用的方法是ScheduleAtFixedRate(基于固定时间间隔进行任务调度)和ScheduleWithFixedDelay(基于不固定时间间隔进行任务调度,主要取决于任务执行时间长短), demo代码如下:

    复制代码
    public static void main(String[] args) {

        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                log.warn("schedule run");
            }
        },3,TimeUnit.SECONDS);
    
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                log.warn("schedule run");
            }
        },1,3,TimeUnit.SECONDS);
        
    }
    

    复制代码
      

    d、newSingleThreadExecutor:创建一个单线程化的线程池,只会用唯一的线程来执行任务

    newSingleThreadExecutor将corePoolSize和maximumPoolSize都固定为1,线程空闲时直接销毁,使用的LinkedBlockingQueue.demo代码如下:

    复制代码
    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();
    }
    

    复制代码

    线程池合理配置
    线程池的具体配合需要按照实际情况进行调整,以下为两条参考原则,可先按此原则设置再根据系统负载,资源利用情况进行调整。

    a、cpu密集型任务,需要尽量压榨cpu,参考值可以设置为ncpu+1;
    
    b、io密集型任务,参考值可以设置为2*ncpu
    

    本系列博客设计了并发基础、线程安全性、安全发布对象,线程安全策略,J.U.C组件,线程池,高并发扩展等方面的内容

    http://www.cnblogs.com/sbrn/category/1207947.html

  • 相关阅读:
    2020 ICPC 沈阳站 M. United in Stormwind
    [笔记] 中世纪反演魔法
    [模板] 多项式工业
    HDU 1187 Big Event in HDU
    洛谷 P2000 拯救世界
    HDU 1085 Holding Bin-Laden Captive!
    HDU 1028 Ignatius and the Princess III
    性能测试——jmeter接口测试复习——请求元件之配置元件——参数化——txt与csv
    性能测试——jmeter接口测试复习——请求元件之用户自定义变量——即参数化
    性能测试——jmeter接口测试复习——断言之响应断言——放在某个http请求下面,用来判断返回code、header、message中是否包含预期的值
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/9000916.html
Copyright © 2011-2022 走看看