• java线程池学习


    最近想研究下线程的使用,但是如果自己创建一个线程,太费系统性能 ,所以打算自己写个线程池

    参考博客:https://www.cnblogs.com/dolphin0520/p/3932921.html

    参考书籍:<<java并发编程的艺术>>

    一 Java中的ThreadPoolExecutor类

     java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。

      在ThreadPoolExecutor类中提供了四个构造方法:

    public class ThreadPoolExecutor extends AbstractExecutorService {
        .....
        public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                BlockingQueue<Runnable> workQueue);
     
        public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
     
        public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
     
        public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
        ...
    }

    从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。

    下面解释下一下构造器中各个参数的含义:

    • corePoolSize: (线程池的基本大小) 当提交一个任务到线程池时,线程池会创建一个线程来执行任务,如果线程池的有空闲线程但是数量小于 corePoolSize时,也会创建一个线程来执行,等到需要执行的任务数 大于线程池基本大小的时候就不会再创建, 如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
    • maximumPoolSize:(线程池最大数量):线程池允许创建的最大线程数,如果队列满了.并且已经创建的线程数量小于最大线程数,则线程池会创建新的线程执行任务.值得注意的是,如果使用了无界的任务队列这个参数就没有效果了
    • keepAliveTime:(线程活动保持时间) :表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,就是说线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;如果任务很多,并且每个任务执行的时间比较短时,可以调大时间,提高线程的利用率
    • TimeUnit:
    TimeUnit.DAYS;               //
    TimeUnit.HOURS;             //小时
    TimeUnit.MINUTES;           //分钟
    TimeUnit.SECONDS;           //
    TimeUnit.MILLISECONDS;      //毫秒
    TimeUnit.MICROSECONDS;      //微妙
    TimeUnit.NANOSECONDS;       //纳秒
    • workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
      • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原则对元素进行排序。
      • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
      • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列

      • PriorityBlockingQueue:一个具有优先级的无限阻塞队列
    • RejectedExecutionHandler handler(饱和策略):表示当拒绝处理任务时的策略,有以下四种取值:
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 
    
    • ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线程设置有意义的名字,代码如下

        new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();

      以上是线程池的基本概念

    二 线程池内部原理实现

    1.在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。

      workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

      1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

      2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

      3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

    2.线程池的状态

    runState

     // runState is stored in the high-order bits
        private static final int RUNNING    = -1 << COUNT_BITS;
        private static final int SHUTDOWN   =  0 << COUNT_BITS;
        private static final int STOP       =  1 << COUNT_BITS;
        private static final int TIDYING    =  2 << COUNT_BITS;
        private static final int TERMINATED =  3 << COUNT_BITS;

      如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

      如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;

      当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

     3.线程池获取资源情况

      要清楚corePoolSize和maximumPoolSize的含义;

      其次,要知道Worker是用来起到什么作用的;

      要知道任务提交给线程池之后的处理策略,这里总结一下主要有4点:

    • 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
    • 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
    • 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

      如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

    4.线程池的关闭

    ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

    • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    线程池关闭的原理是遍历线程池中的工作先,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法停止

    shutdownNow首先将线程池的状态设置为stop,然后尝试停止所有正在执行或者暂停任务的线程

    shutdown只是将线程池的状态设置成shutdown状态,然后中断所有没有正在执行的任务的线程

      只要调用了这两个关闭方法中的任意一个,isShutdown方法就会返回true。当所有的任务
    都已关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。至于应该调用哪
    一种方法来关闭线程池,应该由提交到线程池的任务特性决定,通常调用shutdown方法来关闭
    线程池,如果任务不一定要执行完,则可以调用shutdownNow方法

    5.线程的提交

      execute()用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功

      submit()用户提交需要返回值的任务,线程池会返回一个future类型的对象,通过这个对象判断是否执行成功,future.get()方法 get方法会阻塞当前线程知道任务完成,,而是用get(long time,timeuitl unit )会阻塞一段时间后返回,这个时间有可能没有执行完任务

    Future<Object> future = executor.submit(harReturnValuetask);
    try {
    Object s = future.get();
    } catch (InterruptedException e) {
    // 处理中断异常
    } catch (ExecutionException e) {
    // 处理无法执行任务异常
    } finally {
    // 关闭线程池
    executor.shutdown();
    }

    三 使用实例

     /**
         * corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线
         * 程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任
         * 务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,
         * 线程池会提前创建并启动所有基本线程。
         */
        private static  int corePoolSize  =5;
        /**
         * (线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并
         * 且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如
         * 果使用了无界的任务队列这个参数就没什么效果
         */
    
        private static  int maximumPoolSize  =10;
    
        /**
         * (线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以,
         * 如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率
         * @param args
         */
        private static long keepAliveTime =200;
    
        public static void main(String[] args) {
    
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5));
    
            for (int i = 0; i <20 ; i++) {
    
                executor.submit(new Mytask(i));
                System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                        executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
            }
            executor.shutdown();
        }
    }
    
    
    class Mytask implements Runnable{
    
        private int a;
    
        public Mytask(int a) {
            this.a = a;
        }
    
        @Override
        public void run() {
            System.out.println(a+"任务执行");
            try {
                Thread.currentThread().sleep(4000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(a+"执行完毕");
        }
    /Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61838:/Applications/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath /Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home/lib/tools.jar:/Users/xuyuanfang/IdeaProjects/threaddemo/target/classes:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-web/2.1.3.RELEASE/spring-boot-starter-web-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter/2.1.3.RELEASE/spring-boot-starter-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot/2.1.3.RELEASE/spring-boot-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-autoconfigure/2.1.3.RELEASE/spring-boot-autoconfigure-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-logging/2.1.3.RELEASE/spring-boot-starter-logging-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar:/Users/xuyuanfang/apps/maven_jar/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/logging/log4j/log4j-to-slf4j/2.11.2/log4j-to-slf4j-2.11.2.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/logging/log4j/log4j-api/2.11.2/log4j-api-2.11.2.jar:/Users/xuyuanfang/apps/maven_jar/org/slf4j/jul-to-slf4j/1.7.25/jul-to-slf4j-1.7.25.jar:/Users/xuyuanfang/apps/maven_jar/javax/annotation/javax.annotation-api/1.3.2/javax.annotation-api-1.3.2.jar:/Users/xuyuanfang/apps/maven_jar/org/yaml/snakeyaml/1.23/snakeyaml-1.23.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-json/2.1.3.RELEASE/spring-boot-starter-json-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-databind/2.9.8/jackson-databind-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-annotations/2.9.0/jackson-annotations-2.9.0.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/core/jackson-core/2.9.8/jackson-core-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.9.8/jackson-datatype-jdk8-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.9.8/jackson-datatype-jsr310-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/jackson/module/jackson-module-parameter-names/2.9.8/jackson-module-parameter-names-2.9.8.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/boot/spring-boot-starter-tomcat/2.1.3.RELEASE/spring-boot-starter-tomcat-2.1.3.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-el/9.0.16/tomcat-embed-el-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.16/tomcat-embed-websocket-9.0.16.jar:/Users/xuyuanfang/apps/maven_jar/org/hibernate/validator/hibernate-validator/6.0.14.Final/hibernate-validator-6.0.14.Final.jar:/Users/xuyuanfang/apps/maven_jar/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar:/Users/xuyuanfang/apps/maven_jar/org/jboss/logging/jboss-logging/3.3.2.Final/jboss-logging-3.3.2.Final.jar:/Users/xuyuanfang/apps/maven_jar/com/fasterxml/classmate/1.4.0/classmate-1.4.0.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-web/5.1.5.RELEASE/spring-web-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-beans/5.1.5.RELEASE/spring-beans-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-webmvc/5.1.5.RELEASE/spring-webmvc-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-aop/5.1.5.RELEASE/spring-aop-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-context/5.1.5.RELEASE/spring-context-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-expression/5.1.5.RELEASE/spring-expression-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/projectlombok/lombok/1.18.6/lombok-1.18.6.jar:/Users/xuyuanfang/apps/maven_jar/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-core/5.1.5.RELEASE/spring-core-5.1.5.RELEASE.jar:/Users/xuyuanfang/apps/maven_jar/org/springframework/spring-jcl/5.1.5.RELEASE/spring-jcl-5.1.5.RELEASE.jar com.xiaodao.threaddemo.thread.ExecutorsTest
    0任务执行
    Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@e9e54c2 rejected from java.util.concurrent.ThreadPoolExecutor@65ab7765[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
    线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
    线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
    1任务执行
    线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
    2任务执行
    线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
    3任务执行
    线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
    4任务执行
    线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
    线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
    线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
    线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
    线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
        at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
    10任务执行
    线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
    线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
    11任务执行
    12任务执行
    线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
        at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    13任务执行
        at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
    14任务执行
        at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
        at com.xiaodao.threaddemo.thread.ExecutorsTest.main(ExecutorsTest.java:41)
    1执行完毕
    4执行完毕
    0执行完毕
    2执行完毕
    8任务执行
    3执行完毕
    9任务执行
    7任务执行
    6任务执行
    13执行完毕
    5任务执行
    11执行完毕
    14执行完毕
    12执行完毕
    10执行完毕
    9执行完毕
    5执行完毕
    8执行完毕
    7执行完毕
    6执行完毕
    
    Process finished with exit code 130 (interrupted by signal 2: SIGINT)
    View Code

    我们可以看到当执行线程大于corePoolSize 时就会将任务放入缓存队列中取,但是我们把任务数放大程序就会拒绝执行,并报错

    当我们换一个饱和策略就不会报错,工作中.

        public static void main(String[] args) {
    
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,
                    maximumPoolSize, keepAliveTime, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5),
                    new ThreadPoolExecutor.CallerRunsPolicy());
    
            for (int i = 0; i <20 ; i++) {
    
                executor.submit(new Mytask(i));
                System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                        executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
            }
            executor.shutdown();
        }

    使用Executors类中提供的几个静态方法来创建线程池:

    不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池

    Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
    Executors.newSingleThreadExecutor();   //创建容量为1的缓冲池
    Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池

    一下是这个三个方法的具体实现

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。

      newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

      newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;

      newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。

      实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。

      另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。

     四 配置线程池的大小

      要想合理的配置线程池,首先要分析任务的特性,可以重以下几个点击来分析:

    •   任务的性质:CPU密集型任务,IO密集型任务,和混合型任务
    • 任务的优先级:高 中和低
    • 任务的执行时间:长 中和短
    • 任务的依赖性:是否依赖其他资源,如,数据连接,调用别的系统接口

    CPU密集型任务尽可能配置小的线程,如配置Ncpu+1个线程的线程池

    IO密集型任务线程并不是一直在执行任务:应该配置更多的线程,如 2* ncpu

    可以通过 Runtime.getRuntime().availableProcessors()获取当前设备的cup个数

    依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU

    在spring中使用线程池

    目前使用spring 中的线程池有点问题.自定义的线程池倒是可以使用,此处贴出代码

    @Configuration
    public class TheadPoolTask {
        /**
         * 获取线程池
         *
         * @return
         */
        @Bean
        public ThreadPoolExecutor threadPoolTaskExecutor(){
    
            ThreadPoolExecutor executor = new ThreadPoolExecutor(12,
                    16, 12, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>(5),
                    new ThreadPoolExecutor.CallerRunsPolicy());
    
            return   executor;
        }
    }

    测试类:

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @ContextConfiguration(locations = {"/spring-context.xml","/spring-context-jedis.xml","/spring-context-shiro.xml"})
    public class ThreadTest {
    
        @Resource(name = "threadPoolTaskExecutor")
        private ThreadPoolExecutor taskExecutor;
    
        public static void main(String[] args) {
            System.out.println(Runtime.getRuntime().availableProcessors());
        }
    
    
        @Test
        public  void test01(){
            try {
            for (int i = 0; i <25 ; i++) {
    
            taskExecutor.submit(new Test01(i));
                System.out.println("线程池中线程数目:"+taskExecutor.getPoolSize()+",队列中等待执行的任务数目:"+
                        taskExecutor.getQueue().size()+",已执行玩别的任务数目:"+taskExecutor.getCompletedTaskCount());
            }
            CountDownLatch c = new CountDownLatch(1);
    
                c.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    
    class Test01 implements Runnable{
    
        private int a;
    
        public Test01(int a) {
            this.a = a;
        }
    
        @Override
        public void run() {
            try {
                System.out.println("线程开始"+ Thread.currentThread().getName()+"------"+a);
                Thread.sleep(5000);
                System.out.println("线程结束 "+ Thread.currentThread().getName()+"------"+a);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
  • 相关阅读:
    iptables防火墙(RHEL6)
    漏洞扫描与网络抓包
    服务安全与监控
    Typecho反序列化漏洞
    python类,魔术方法等学习&&部分ssti常见操作知识点复习加深
    PHAR伪协议&&[CISCN2019 华北赛区 Day1 Web1]Dropbox
    [GXYCTF2019]禁止套娃 1 &无参数RCE
    PHP代码审计学习(1)
    Yii2安装完kartik组件后,使用时报错
    收藏博客
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/10622613.html
Copyright © 2020-2023  润新知