• 多线程编程(六)-Executor与ThreadPoolExecutor的使用


    • 使用Executors工厂类创建线程池

        1、使用newCachedThreadPool()方法创建无界线程池

          newCachedThreadPool()方法创建的是无界线程池,可以进行线程自动回收,此类线程池中存放线程个数理论值为Integer.MAX_VALUE最大值。

    package com.wjg.unit4_2_2;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run {
        public static void main(String[] args) throws InterruptedException {
            Run run = new Run();
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < 5; i++) {
                executorService.execute(run.new MyRunnable(" "+(i+1)));
            }
            Thread.sleep(3000);
            System.out.println();
            System.out.println();
            for (int i = 0; i < 5; i++) {
                executorService.execute(run.new MyRunnable(" "+(i+1)));
            }
        }
        
        
        public class MyRunnable implements Runnable{
            private String username;
            
            public MyRunnable(String username) {
                this.username = username;
            }
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
                System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
            }
            
        }
    }

    执行结果:

    pool-1-thread-1 username= 1 begin 1488268086641

    pool-1-thread-3 username= 3 begin 1488268086641

    pool-1-thread-2 username= 2 begin 1488268086641

    pool-1-thread-2 username= 2 end   1488268086641

    pool-1-thread-4 username= 4 begin 1488268086642

    pool-1-thread-4 username= 4 end   1488268086642

    pool-1-thread-3 username= 3 end   1488268086641

    pool-1-thread-1 username= 1 end   1488268086641

    pool-1-thread-5 username= 5 begin 1488268086642

    pool-1-thread-5 username= 5 end   1488268086642

     

     

    pool-1-thread-5 username= 1 begin 1488268089647

    pool-1-thread-3 username= 3 begin 1488268089648

    pool-1-thread-4 username= 4 begin 1488268089648

    pool-1-thread-1 username= 2 begin 1488268089647

    pool-1-thread-1 username= 2 end   1488268089648

    pool-1-thread-4 username= 4 end   1488268089648

    pool-1-thread-3 username= 3 end   1488268089648

    pool-1-thread-2 username= 5 begin 1488268089648

    pool-1-thread-2 username= 5 end   1488268089648

    pool-1-thread-5 username= 1 end   1488268089648

     

    通过线程的名字,可以看出来线程是从池中取出来的,是可以复用的。

        2、使用newCachedThreadPool(ThreadFactory)定制线程工厂

          构造函数ThreadFactory是实现定制Thread的作用,具体可以看下面的例子。

    package com.wjg.unit4_2_3;
    
    import java.util.Date;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    public class Run {
        public static void main(String[] args) {
            Run run = new Run();
            MyThreadFactory factory = run.new MyThreadFactory();
            ExecutorService executorService = Executors.newCachedThreadPool(factory);
            executorService.execute(new Runnable() {
                
                @Override
                public void run() {
                    System.out.println("当前线程的自定义名称为:"+ Thread.currentThread().getName());
                }
            });
        }
        
        
        public class MyThreadFactory implements ThreadFactory{
    
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("自定义名称:"+new Date());
                return thread;
            }
            
        }
    }

    执行结果:

    当前线程的自定义名称为:自定义名称:Tue Feb 28 15:58:13 CST 2017

        3、使用newFixedThreadPool(int) 方法创建有界线程池

          此方法创建的是有界线程池,也就是池中的线程的个数可以指定最大值。

    package com.wjg.unit4_2_4;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run {
        public static void main(String[] args) {
            Run run = new Run();
            ExecutorService executorService = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 5; i++) {
                executorService.execute(run.new MyRunnable(" "+(i+1)));
            }
        }
        
        public class MyRunnable implements Runnable{
            private String username;
            
            public MyRunnable(String username) {
                this.username = username;
            }
    
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
            }
            
        }
    }

    执行结果:

    pool-1-thread-1 username= 1 begin 1488269132995

    pool-1-thread-3 username= 3 begin 1488269132995

    pool-1-thread-2 username= 2 begin 1488269132995

    pool-1-thread-2 username= 2 end   1488269136000

    pool-1-thread-3 username= 3 end   1488269136000

    pool-1-thread-2 username= 4 begin 1488269136000

    pool-1-thread-3 username= 5 begin 1488269136000

    pool-1-thread-1 username= 1 end   1488269136000

    pool-1-thread-2 username= 4 end   1488269139002

    pool-1-thread-3 username= 5 end   1488269139002

    通过执行结果可以看出,线程池中的线程最大数量为3。

        4、使用newSingleThreadExecutor()方法创建单一线程池

          此方法可以创建单一线程池,线程池里只有一个线程,单一线程池可以实现以队列的方式来执行任务。

    package com.wjg.unit4_2_5;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class Run {
        public static void main(String[] args) {
            Run run = new Run();
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 5; i++) {
                executorService.execute(run.new MyRunnable(" "+(i+1)));
            }
        }
        
        
        public class MyRunnable implements Runnable{
            private String username;
            
            public MyRunnable(String username) {
                this.username = username;
            }
    
    
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" username="+username+" begin "+System.currentTimeMillis());
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" username="+username+" end   "+System.currentTimeMillis());
            }
            
        }
    }

    执行结果:

    pool-1-thread-1 username= 1 begin 1488269392403

    pool-1-thread-1 username= 1 end   1488269395409

    pool-1-thread-1 username= 2 begin 1488269395409

    pool-1-thread-1 username= 2 end   1488269398412

    pool-1-thread-1 username= 3 begin 1488269398413

    pool-1-thread-1 username= 3 end   1488269401418

    pool-1-thread-1 username= 4 begin 1488269401418

    pool-1-thread-1 username= 4 end   1488269404422

    pool-1-thread-1 username= 5 begin 1488269404422

    pool-1-thread-1 username= 5 end   1488269407423

     

    由执行结果的线程名字可以看出,线程池中只有一个线程。

    • ThreadPoolExecutor的使用

        类ThreadPoolExecutor可以非常方便的创建线程池对象。

        常用的构造方法有ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

        参数解释如下:

        corePoolSize:池中所保存的线程数,包括空闲线程数,也就是核心池的大小。

        maximumPoolSize:池中允许的最大线程数

        keepAliveTime:当线程数量大于corePoolSize值时,在没有超过指定的时间内是不从线程池中将空闲线程删除的,如果超过此时间,则删除。

        unit:keepAliveTime参数的时间单位。

        workQueue:执行前用于保持任务的队列。此队列仅保存由execute方法提交的Runnable任务。

        为了更好地理解这些参数在使用上的一些关系,可以将它们进行详细化的注释:

        (1)A代表execute(runnable)欲执行的runnable的数量;

        (2)B代表corePoolSize;

        (3)C代表maximumPoolSize;

        (4)D代表A-B(假设A>=B);

        (5)E代表newLinkedBlockingDeque<Runnable>()队列,无构造函数。

        (6)F代表SynchronousQueue队列;

        (7)G代表keepAliveTime;

        在使用线程池的过程下,会出现以下的集中情况:

        (1)如果A<=B,那么马上创建线程运行这个任务,并不放入扩展队列Queue中,其他参数功能忽略;

        (2)如果A>B&&A<=C&&E,则C和G参数忽略,并把D放入E中等待被执行;

        (3)如果A>B&&A<=C&&F,则C和G参数有效,并且马上创建线程运行这些任务,而不把D放入F中,D执行完任务后在指定时间后发生超时时将D进行清除。

        (4)如果A>B&&A>C&&E,则C和G参数忽略,并把D放入E中等待被执行;

        (5)如果A>B&&A>C&&F,则处理C的任务,其他任务则不再处理抛出异常;

        方法getActiveCount()的作用是取得有多少个线程正在执行任务。

        方法getPoolSize()的作用是获得当前线程池里面有多少个线程,这些线程数包括正在执行任务的线程,也包括正在休眠的线程。

        方法getCompletedTaskCount()的作用是取得有多少个线程已经执行完任务了。

        方法getCorePoolSize()的作用是取得构造方法传入的corePoolSize参数值。

        方法getMaximumPoolSize()的作用是取得构造方法传入的maximumPoolSize的值。

        方法getTaskCount()的作用是取得有多少个任务发送给了线程池。

        方法shutdown()的作用是使当前未执行完的线程继续执行,而不再添加新的任务task,该方法不会阻塞,调用之后,主线程main马上结束,而线程池会继续运行直到所有任务执行完才会停止。

        方法shutdownNow()的作用是中断所有的任务task,并且抛出InterruptedException异常,前提是在Runnable中使用if(Thread.currentThread().isInterrupted()==true)语句来判断当前线程的中断状态,而未执行的线程不再执行,也就是从执行队列中清除。如果不手工加if语句及抛出异常,则池中正在运行的线程知道执行完毕,而未执行的线程不再执行,也从执行队列中清除。

        方法isShutDown()的作用是判断线程池是否已经关闭。  

        方法isTerminating()的作用是判断线程池是否正在关闭中。

        方法isTerminated()的作用是判断线程池是否已经关闭。

        方法awaitTermination(long timeout,TimeUnit unit)的作用是查看在指定的时间之内,线程池是否已经终止工作,也就是最多等待多少时间后去判断线程池是否已经终止工作。

        方法allowsCoreThreadTimeOut(boolean) 的作用是配置核心线程是否有超时的效果。

        方法prestartCoreThread()的作用是每调用一次就创建一个核心线程,返回值为boolean。

        方法prestartAllCoreThreads()的作用是启动全部核心线程,返回值是启动核心线程的数量。

    • 线程池ThreadPoolExecutor的拒绝策略

        线程池中的资源全部被占用的时候,对新添加的Task任务有不同的处理策略,在默认的情况ThreadPoolExecutor类中有4个不同的处理方式:

        (1)AbortPolicy:当任务添加到线程池中被拒绝时,它将抛出RejectedExecutionException异常。

        (2)CallerRunsPolicy:当任务添加到线程池被拒绝时,会使用调用线程池的Thread线程对象处理被拒绝的任务。

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

        (4)DiscardPolicy:当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

  • 相关阅读:
    Spring MVC @RequestMapping注解详解
    (转)Cesium教程系列汇总
    spring boot +mybatis(通过properties配置) 集成
    SpringBoot2.0之四 简单整合MyBatis
    在Windows下使用Git+TortoiseGit+码云管理项目代码
    TortoiseGit之配置密钥
    Spring Boot 学习之路二 配置文件 application.yml
    SpringBoot学习笔记(2) Spring Boot的一些配置
    【入门】Spring-Boot项目配置Mysql数据库
    Spring 的application.properties项目配置与注解
  • 原文地址:https://www.cnblogs.com/niceplay/p/6479698.html
Copyright © 2020-2023  润新知