精华内容
下载资源
问答
  • java中Executors创建线程池的三种方法

    万次阅读 2020-04-04 17:10:59
    个关键线程池的比较 1、创建单个线程(单例模式) public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, ...

    三个关键线程池的比较

    1、创建单个线程(单例模式)

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    

    由于调用的ThreadPoolExecutor的构造方法的核心池大小和最大线程池均为1,所以该线程池只能存在一个线程。

    2、创建固定大小的线程池

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    

    我们可以看到,该创建线程池的方法提供了一个课传递的参数nThreads,该参数可以设置线程池的核心大小以及最大容量,即该线程池的固定大小。

    3、可以伸缩的线程池

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    

    我们可以看到,这里的最大线程大小可以达到Integer的最大大小2147483647,一般情况下我们是用不了这么多线程的,想用这么多的线程,是需要强大的硬件设备的支持,我们可以看到在线程被创建后60s如果没有被调用就会被释放,关于这些参数我会在后面讲到。

    这几种方式创建的线程池都是通过创建ThreadPoolExecutor对象来实现的

    4、ThreadPoolExecutor

    我们来看一下这三个线程池创建时调用的ThreadPoolExecutor的构造方法

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    

    我们看下这里面调用的this方法

    public ThreadPoolExecutor(int corePoolSize,	// 核心线程池大小
                              int maximumPoolSize,	// 最大线程池大小
                              long keepAliveTime,	// 超时了没有人调用就会释放
                              TimeUnit unit,	// 超时单位
                              BlockingQueue<Runnable> workQueue,	// 阻塞队列
                              ThreadFactory threadFactory,	// 线程工厂,创建线程线程的
                              RejectedExecutionHandler handler) {// 拒绝策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
            null :
        AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
    

    所以我们看到这些方法本质上都是调用了ThreadPoolExecutor的构造方法

    注意:虽然用Executors创建线程池可以方便我们的操作,但是阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,所以建议大家还是用ThreadPoolExecutor 来创建线程池

    ThreadPoolExecutor创建线程池参考https://blog.csdn.net/jjj___jjj/article/details/105314542

    展开全文
  • package ThreadPool; import java.util.concurrent.*;... * 创建线程池的三种常用方法 */ public class ThreadPoplDemo { public static void main(String args[]) { CreateThreadPool1 (); } public...
    package ThreadPool;
    
    import java.util.concurrent.*;
    
    /**
     * 创建线程池的三种常用方法
     */
    public class ThreadPoplDemo {
        public static void main(String args[]) {
            CreateThreadPool1 ();
        }
    
    
        public static void CreateThreadPool1 (){
            //线程池里面的线程用完以后,还给线程池,不关闭;
            ExecutorService service = new ThreadPoolExecutor(
                    /**
                     * 核心线程数,当线程池初次创建时,是没有任何线程的;当有
                     * 请求发起时,线程池会创建核心线程, 在球球过程中,无论核
                     * 心线程是否闲置,线程池都会创建核心线程,知道满足数量为止
                     */
                    5,
                    /**
                     * 最大线程数,先有的核心线程,后面创建的为临时线程
                     */
                    10,
                    /**
                     * 存活时间。临时线程的存活时间,就是指临时线程闲置后的时间,时间一到就会销毁线程;
                     */
                    3000,
                    /**
                     * 时间单位,一般用毫秒
                     */
                    TimeUnit.MILLISECONDS,
                    /**
                     * 等待队列,用于存放还会处理的请求;
                     */
                    new ArrayBlockingQueue<Runnable>(10),
                    /**
                     * 拒绝服务器助手
                     */
                    new RejectedExecutionHandler() {
                        @Override
                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                System.out.print("请稍后尝试");
                        }
                    }
            );
            //通过线程池启动线程
            for (int i = 0;i<21;i++) {
                service.execute(new ClientRunble());
            }
            /**
             * 关闭线程池,当调用这个方法时,里面的线程不会立即销毁,只是不接受外部请求;
             * 只有内部的线程处理完后,才会销毁,使用线程池的好处是避免线程的频繁创建何销毁;
             * 从而节省cpu性能;
             */
            service.shutdown();
        }
    
    
        public  static void  CreateThreadPool2(){
            /**
             * 此种方法也可以创建线程池,但是此方法创建的线程池没有核心线程;
             * 只有临时线程;
             * 队列是同步队列;
             * 最大线程数无界;
             * 可以很好的响应客户端请求,大池子,小队列;
             * 因为不需要排队等待;
             * 这种线程池适用于短请求,如果都是长请求,可能导致线程一直创建二不销毁,最后造成内存溢出;
             *
             */
            ExecutorService service = Executors.newCachedThreadPool();
        }
    
        public void CreateThreadPool3(){
            /**
             *  小池子大队列:
             *  1.都是核心线程,没有临时线程;
             *  2.队列是无界队列,LinkedBlockingQueue<Runnable>()
             *  3.应用场景:这种线程池的作用:消峰限流。
             *  4.坏处:某些请求可能等待很长时间才处理,不能及时响应客户端;
             */
            ExecutorService service = Executors.newFixedThreadPool(10);
    
        }
    }
    
    
    class ClientRunble implements Runnable{
    
        @Override
        public void run() {
            System.out.println("处理客户请求");
            try {
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    展开全文
  • 创建线程池的方法

    2020-11-10 18:36:56
    一、创建线程池的三种方法 Executors.newSingleThreadExecutor(); //单个线程 Executors.newFixedThreadPool(5); //创建一个固定的线程池 Executors.newCachedThreadPool(); //创建一个可伸缩的线程池 1....

    创建线程池的方法

    一、创建线程池的三种方法

    Executors.newSingleThreadExecutor(); //单个线程
    Executors.newFixedThreadPool(5); //创建一个固定的线程池
    Executors.newCachedThreadPool(); //创建一个可伸缩的线程池

    1.newSingleThreadExecutor

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecuterTest1 {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newSingleThreadExecutor();  //单个线程
            try {
                for(int i=0;i<10;i++) {
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName()+" ok");
                    });
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                //关闭线程池
                threadPool.shutdown();
            }
    
        }
    }
    

    2. newFixedThreadPool

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecuterTest1 {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建一个固定的线程池
    
            try {
                for(int i=0;i<10;i++) {
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName()+" ok");
                    });
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                //关闭线程池
                threadPool.shutdown();
            }
    
        }
    }
    

    3. newCachedThreadPool

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class ExecuterTest1 {
        public static void main(String[] args) {
            ExecutorService threadPool = Executors.newCachedThreadPool();  //创建一个可伸缩的线程池
            try {
                for(int i=0;i<10;i++) {
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName()+" ok");
                    });
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                //关闭线程池
                threadPool.shutdown();
            }
    
        }
    }
    

    二、三种方法的源码

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    //#############################################################
    
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    //############################################################
    
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  //约等于20亿
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    • 以上三种方法中都调用ThreadPoolExecutor来创建线程池。
    • 但三种方法都存在一定的弊端:
    • (1)SingleThreadExecutor和FixedThreadPool允许的请求队列长度为Integer.MAX.VALUE,可能会导致OOM
    • (2)CachedThreadPool允许的创建线程数量为Integer.MAX.VALUE,可能会导致OOM

    ThreadPoolExecutor源码分析

        public ThreadPoolExecutor(int corePoolSize,     //核心线程池大小
                                  int maximumPoolSize,  //最大核心线程池大小
                                  long keepAliveTime,   //超时了没有人调用就会释放
                                  TimeUnit unit,   //超时单位
                                  BlockingQueue<Runnable> workQueue,   //阻塞队列
                                  ThreadFactory threadFactory,        //线程工厂,创建线程的,一般不用动
                                  RejectedExecutionHandler handler) {   //拒绝策略
            if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    
    展开全文
  • 文章目录Java创建线程的三种方式线程池的优点Java中创建线程池的两种方式Executors工厂方法创建new ThreadPoolExecutor()自定义创建corePoolSize和maximumPoolSizeworkQueue任务队列拒绝策略 Java创建线程的三种方式...

    Java创建线程的三种方式

    1. 继承Thread类创建线程类
    2. 实现Runnable接口
    3. 通过Callable和Future创建线程

    线程池的优点

    • 重用存在的线程,减少对象创建、消亡的开销,性能佳
    • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
    • 提供定时执行、定期执行、单线程、并发数控制等功能。

    Java中创建线程池的两种方式

    • 通过Executors工厂方法创建
    • 通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)自定义创建

    Executors工厂方法创建

    • newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
      	@Test
        public void test() {
            //创建使用单个线程的线程池
            ExecutorService es1 = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 10; i++) {
                es1.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + "正在执行任务");
                    }
                });
            }
        }
    
    • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待(长度指定为1就变成了上面的)
        @Test
        public void test2() {
            //创建使用固定线程数的线程池
            ExecutorService es2 = Executors.newFixedThreadPool(3);
    
            for (int i = 0; i < 10; i++) {
                es2.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + "正在执行任务");
                    }
                });
            }
        }
    
    • newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程
        @Test
        public void test3() {
            //创建一个会根据需要创建新线程的线程池
            ExecutorService es3 = Executors.newCachedThreadPool();
            for (int i = 0; i < 20; i++) {
                es3.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + "正在执行任务");
                    }
                });
            }
        }
    
    • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
       @Test
        public void test4() {
            //创建拥有固定线程数量的定时线程任务的线程池
            ScheduledExecutorService es4 = Executors.newScheduledThreadPool(2);
            System.out.println("时间:" + System.currentTimeMillis());
            for (int i = 0; i < 5; i++) {
                es4.schedule(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("时间:" + System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "正在执行任务");
                    }
                }, 3, TimeUnit.SECONDS);  //表示延迟3秒执行。
                //1, 3, TimeUnit.SECONDS:表示延迟1秒后每3秒执行一次
                //ScheduledExecutorService比Timer更安全,功能更强大
    
            }
        }
    
    • newSingleThreadScheduledExecutor创建一个单线程线程池,支持定时及周期性任务执行(就是上面定长指定为1)
        @Test
        public void test5() {
            //创建只有一个线程的定时线程任务的线程池
            ScheduledExecutorService es5 = Executors.newSingleThreadScheduledExecutor();
            System.out.println("时间:" + System.currentTimeMillis());
            for (int i = 0; i < 5; i++) {
                es5.schedule(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("时间:" + System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "正在执行任务");
                    }
                }, 3, TimeUnit.SECONDS);
            }
        }
    

    new ThreadPoolExecutor()自定义创建

    在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

     public ThreadPoolExecutor(int corePoolSize, // 1
                                  int maximumPoolSize,  // 2
                                  long keepAliveTime,  // 3
                                  TimeUnit unit,  // 4
                                  BlockingQueue<Runnable> workQueue, // 5
                                  ThreadFactory threadFactory,  // 6
                                  RejectedExecutionHandler handler ) { //7
            if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    
    • corePoolSize 表示核心常驻线程池。即使空闲也会在线程池中保活,除非设置了允许核心线程池超时;
    • maximumPoolSize 表示线程池同时执行的最大线程数量;
    • keepAliveTime 表示线程池中的线程空闲时间,线程在销毁前等待新任务的最大时限;
    • unit 表示 keepAliveTime 的单位;
    • workQueue 存放执行前的任务。只会存放通过 execute 函数提交的 Runnable 任务;
    • threadFactory 创建新线程的工厂;
    • handler 线程超限且队列容量也达到最大值时执行受阻的处理策略。

    上面参数对应的含义如下,比较重要的是corePoolSize、maximumPoolSize、workQueue、handler

    序号 名称 类型 含义
    1 corePoolSize int 核心线程池大小
    2 maximumPoolSize int 最大线程池大小
    3 keepAliveTime long 线程最大空闲时间
    4 unit TimeUnit keepAliveTime时间单位
    5 workQueue BlockingQueue 线程等待队列
    6 threadFactory ThreadFactory 线程创建工厂
    7 handler RejectedExecutionHandler 拒绝策略

    这三个稍微有印象即可,重点是下面的四个!!!

    keepAliveTime: 当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;

    unit: keepAliveTime的单位;

    threadFactory: 线程工厂,用于创建线程,一般用默认即可;

    corePoolSize和maximumPoolSize

    corePoolSize:

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

    maximumPoolSize:

    线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。

    corePoolSize与maximumPoolSize的关系

    首先corePoolSize肯定是 <= maximumPoolSize。

    其他关系如下:

    1. 若当前线程池中线程数 < corePoolSize,则每来一个任务就创建一个线程去执行;
    2. 若当前线程池中线程数 >= corePoolSize,会尝试将任务添加到任务队列。如果添加成功,则任务会等待空闲线程将其取出并执行;
    3. 若队列已满,且当前线程池中线程数 < maximumPoolSize,创建新的线程;
    4. 若当前线程池中线程数 >= maximumPoolSize,则会采用拒绝策略(JDK提供了四种,下面会介绍到)。

    注意:关系3是针对的有界队列,无界队列永远都不会满,所以只有前2种关系。

    这段话详细了描述了线程池对任务的处理流程,这里用个图总结一下

    img

    workQueue任务队列

    它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列;

    1、直接提交队列:设置为SynchronousQueue队列,SynchronousQueue是一个特殊的BlockingQueue,它没有容量,没执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。

    public class ThreadPoolExecutorParam {
        private static ExecutorService pool;
    
        /**
         * 这种直接提交队列相当于没有缓存数量,提交的任务不会被保存,总是会马上提交执行,所以超过core以后,最多就还能创建max-core数量个线程
         */
        @Test
        public void test1() {
    
            pool = new ThreadPoolExecutor(2, 5, 1000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),
                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            for (int i = 0; i < 6; i++) {
                pool.execute(new ThreadTask());
            }
        }
    }    
    

    2、有界的任务队列:有界的任务队列可以使用ArrayBlockingQueue实现,如下所示

    使用ArrayBlockingQueue有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。

      /**
         * 参数  core:2   max:5    queue:10
         * 1、 如果超过15个线程就会执行拒绝,总共只有5个线程执行,也就是超过12个以后,会最多创建3个线程执行,所以就是最多15个线程创建
         * 2、如果创建的线程数量小于12个,就始终只有2个线程在执行
         * 3、也就是说超出的数量大于core+queue=12的线程,会根据max-core的数量=3,新创建最多3个线程执行
         * 4、如果设置的队列容量足够大,也跟无界队列一个意思?
         */
        @Test
        public void test2() {
            pool = new ThreadPoolExecutor(2, 5, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),
                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            for (int i = 0; i < 13; i++) {
                pool.execute(new ThreadTask());
            }
        }
    

    3、无界的任务队列:有界任务队列可以使用LinkedBlockingQueue实现,如下所示

    使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题。

        @Test
        public void test3() {
            pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
            for (int i = 0; i < 13; i++) {
                pool.execute(new ThreadTask());
            }
    
        }
    

    4、优先任务队列: 优先任务队列通过PriorityBlockingQueue实现,下面我们通过一个例子演示下

    public class ThreadPool {
        private static ExecutorService pool;
        public static void main( String[] args )
        {
            //优先任务队列
            pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
              
            for(int i=0;i<20;i++) {
                pool.execute(new ThreadTask(i));
            }    
        }
    }
    
    public class ThreadTask implements Runnable,Comparable<ThreadTask>{
        
        private int priority;
        
        public int getPriority() {
            return priority;
        }
    
        public void setPriority(int priority) {
            this.priority = priority;
        }
    
        public ThreadTask() {
            
        }
        
        public ThreadTask(int priority) {
            this.priority = priority;
        }
    
        //当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高
        public int compareTo(ThreadTask o) {
             return  this.priority>o.priority?-1:1;
        }
        
        public void run() {
            try {
                //让线程阻塞,使后续任务进入缓存队列
                Thread.sleep(1000);
                System.out.println("priority:"+this.priority+",ThreadName:"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        
        }
    }
    

    执行结果:

    priority:0,ThreadName:pool-1-thread-1
    priority:9,ThreadName:pool-1-thread-1
    priority:8,ThreadName:pool-1-thread-1
    priority:7,ThreadName:pool-1-thread-1
    priority:6,ThreadName:pool-1-thread-1
    priority:5,ThreadName:pool-1-thread-1
    priority:4,ThreadName:pool-1-thread-1
    priority:3,ThreadName:pool-1-thread-1
    priority:2,ThreadName:pool-1-thread-1
    priority:1,ThreadName:pool-1-thread-1
    

    大家可以看到除了第一个任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,也就是只有一个。

    通过运行的代码我们可以看出PriorityBlockingQueue它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

    拒绝策略

    一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况。ThreadPoolExecutor自带的拒绝策略如下:

    1、AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作;

    2、CallerRunsPolicy策略:如果线程池的线程数量达到上限,该策略会把任务队列中的任务放在调用者线程当中运行;

    3、DiscardOledestPolicy策略:该策略会丢弃任务队列中最老的一个任务,也就是当前任务队列中最先被添加进去的,马上要被执行的那个任务,并尝试再次提交;

    4、DiscardPolicy策略:该策略会默默丢弃无法处理的任务,不予任何处理。当然使用此策略,业务场景中需允许任务的丢失;

    以上内置的策略均实现了RejectedExecutionHandler接口,当然你也可以自己扩展RejectedExecutionHandler接口,定义自己的拒绝策略,我们看下示例代码:

    public class ThreadPool {
        private static ExecutorService pool;
        public static void main( String[] args )
        {
            //自定义拒绝策略
            pool = new ThreadPoolExecutor(1, 2, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5),
                    Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.out.println(r.toString()+"执行了拒绝策略");
                    
                }
            });
              
            for(int i=0;i<10;i++) {
                pool.execute(new ThreadTask());
            }    
        }
    }
    
    public class ThreadTask implements Runnable{    
        public void run() {
            try {
                //让线程阻塞,使后续任务进入缓存队列
                Thread.sleep(1000);
                System.out.println("ThreadName:"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        
        }
    }
    

    执行结果:

    com.hhxx.test.ThreadTask@33909752执行了拒绝策略
    com.hhxx.test.ThreadTask@55f96302执行了拒绝策略
    com.hhxx.test.ThreadTask@3d4eac69执行了拒绝策略
    ThreadName:pool-1-thread-2
    ThreadName:pool-1-thread-1
    ThreadName:pool-1-thread-1
    ThreadName:pool-1-thread-2
    ThreadName:pool-1-thread-1
    ThreadName:pool-1-thread-2
    ThreadName:pool-1-thread-1
    

    可以看到由于任务加了休眠阻塞,执行需要花费一定时间,导致会有一定的任务被丢弃,从而执行自定义的拒绝策略;

    使用线程池测试并发

    平常我们写代码的时候,有一些业务逻辑就要考虑在多个线程并发的情况下是否会有问题,这时候就可以采用线程池创建多个线程进行并发访问!!!

    当然这只是单机测试,如果还想测试多台机器测试,可以用docker部署多台进行并发访问!!!!

    直接new线程测试:

    注意:

    • 假设线程里面执行的逻辑要5s,那thread1.start以后一定要睡眠足够的时间,不然很可能导致线程没有执行完毕!!!
    • 这里要睡眠的原因是因为这是跑测试用例,而实际生产部署以后,服务一直在运行,所以不会出现执行不完!!!

    假如第一个线程执行要10s,第二个线程要5s,但是只睡眠了8s,只能执行完第二个线程,导致第一个线程没有执行完毕(正常情况下是直接new的多个线程跑一样的代码,这里只是说明自己跑单元测试的时候要注意的问题)

      	@Test
        public void test() throws InterruptedException {
            //假设线程里面执行的逻辑要10s,那thread1.start一定要睡眠足够的时间,不然很可能没有执行完毕!!!
            Thread thread1 = new Thread(() -> {
                System.out.println("线程1:" + JSON.toJSONString(System.currentTimeMillis()));
                try {
                    Thread.sleep(10000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1:" + JSON.toJSONString(System.currentTimeMillis()));
            });
            //假设线程里面执行的逻辑要10s,那如果只sleep 8S就会导致没有执行完毕!!!
    
            Thread thread2 = new Thread(() -> {
                System.out.println("线程2:" + JSON.toJSONString(System.currentTimeMillis()));
                try {
                    //睡眠5s,模拟执行业务逻辑时间
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2:" + JSON.toJSONString(System.currentTimeMillis()));
            });
    
            thread1.start();
            thread2.start();
            //平常使用单元测试的时候:这里一定要注意睡眠的时间要大于业务逻辑处理的时间,不然会直接结束程序,导致线程执行的逻辑没有执行完毕
            Thread.sleep(8000L);
    
        }
    

    输出结果:这里线程1就没有输出Thread-0完成任务,因为执行时间大于10s,但是只睡眠了8s就结束了程序!!

    Thread-0正在执行任务
    Thread-1正在执行任务
    Thread-1完成任务
    

    使用线程池测试:

    	 @Test
        public void test2() throws InterruptedException {
            //创建使用固定线程数的线程池
            ExecutorService es = new ThreadPoolExecutor(2, 5, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(10),
                    Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
    
            for (int i = 0; i < 2; i++) {
                es.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println(Thread.currentThread().getName() + "正在执行任务");
                            //睡眠5s,模拟执行业务逻辑时间
                            Thread.sleep(5000L);
                            System.out.println(Thread.currentThread().getName() + "完成任务");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
     		//平常使用单元测试的时候:这里一定要注意睡眠的时间要大于业务逻辑处理的时间,不然会直接结束程序,导致线程执行的逻辑没有执行完毕
            Thread.sleep(6000L);
    
        }
    

    输出结果:

    pool-1-thread-1正在执行任务
    pool-1-thread-2正在执行任务
    pool-1-thread-1完成任务
    pool-1-thread-2完成任务
    

    参考文章:

    https://www.cnblogs.com/dafanjoy/p/9729358.html

    https://blog.csdn.net/dabusiGin/article/details/105327873

    https://blog.csdn.net/qq_35275233/article/details/87893337

    https://www.cnblogs.com/jxxblogs/p/11655670.html

    https://www.jianshu.com/p/c41e942bcd64

    展开全文
  • 关于三种线程池的三种队列区别:SynchronousQueue、LinkedBlockingQueue 和ArrayBlockingQueue,可以点击链接查看。https://blog.csdn.net/qq_26881739/article/details/80983495 最近线程池用的比较多,感觉挺...
  • 线程池:方法 7大参数 4拒绝策略 线程池的好处: 降低资源的消耗 提高响应的速度 方便管理 线程复用、控制最大并发数、管理线程 方法 一般不同Executors工具类创建线程池,不安全 public class ...
  • 1.线程池 线程池(英语:thread pool):一线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。...线程池的好处: 降低资源的浪费 提高响应的速度 方便管理。 3.
  • 用法讲解Executors创建线程池有多种方式,列举最重要的三种如下://创建一个包含指定数目线程的线程池,如果任务数量多于线程数量,那么没有执行的任务必须等待,直到有任务完成为止。 private static ...
  • 线程池的应用之线程的第三种实现方法 ** package com.fenqing.Thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService...
  • 线程池创建的常见方式和一不常见方式 线程池创建的常见方式有继承Thread 和 实现Runnable接口;一不常见甚至被许多人遗忘不常见方式是Callable接口。下面就来看一看两常见和一不常见。 1....
  • 线程池(重点)一:线程池方法,七大参数,四拒绝策略 池化技术: 01:程序运行,本质 :占用系统资源!优化资源使用!=>池化技术 02:线程池、连接池、内存池、对象池///......创建、销毁。十分...
  • 程序运行就是资源占用,池化就是优化资源利用,线程池,连接池啥创建销毁很耗费时间空间,所以就提前准备好资源,你来那就行,用完换回来。 减少资源消耗,速度提升,还有或就是方便管理,线程复用,就像...
  • 本篇文章主要总结了Java创建线程池的三种方式以及线程池参数的详细说明,对线程池感兴趣的同学可以作为参考学习。1)通过工具类java.util.concurrent.Executors的静态方法来创建Executors此包中所定义的 Executor、...
  • 如果学习线程池的具体实现方法,在ExecutorService中有许多函数要重写,许多参数我也不太懂,所以我只是看了Executors类中个工厂方法创建线程池。 1.newSingleThreadExecutor 这个方法创建的线程池里只能...
  • Executors工厂类创建线程池的底层实现

    千次阅读 多人点赞 2013-05-26 21:01:34
    上一篇介绍了ThreadPoolExecutor的基本使用,现在再来看看...三种创建线程池的工厂方法源码: // 无界线程池 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_
  • 一 通过继承Thread线程创建的方法与执行步骤 /* 1 继承Thread 2重写run方法 3创建线程继承类子类 4 调用start(一个线程只能通过调用一次start来进行启动) */ class MyThread extends Thread{ public MyThread...
  • 线程池还提供了一种方法来约束和管理执行任务时消耗资源(包括线程)。每个ThreadPoolExecutor还维护一些基本统计信息,比如已完成任务数量。 1.2 构造函数说明 它提供了以下几种构造函数: corePoolSize: 核心...
  • 线程池方法: public static void main(String[] args) { Executors.newCachedThreadPool();// 请求多 就创建线程多 Executors....最好使用这种创建方式,可以更直接的明白线程池的参数,上面的方式也
  • 今天看视频自学了java线程池,如果学习线程池的具体实现方法,在ExecutorService中有许多函数要重写,许多参数我也不太懂,所以我只是看了Executors类中个工厂方法创建线程池。 1.newSingleThreadExecutor 这个...
  • Java线程池的创建详解

    2019-10-05 03:33:02
    本篇文章主要总结了Java创建线程池的三种方式以及线程池参数的详细说明,对线程池感兴趣的同学可以作为参考学习。 1)通过工具类java.util.concurrent.Executors的静态方法来创建 Executors此包中所定义的 ...
  • 前言 我相信大家在项目中或多或少的都使用过线程,而线程...这我当然知道了,JDK主要提供了三种创建线程池的方法 Executors.newFixedThreadPool(int nThreads) : 创建固定线程数量的线程池 Executors.newSingleThre...
  • //带延时的线程池 Future<Integer> result = null; try { for (int i = 1; i ; i++) { result = service.schedule(() -> { System.out.print(Thread.currentThread().getName()); return new Random...
  • callable接口和runnable接口类似,runnable接口重写了run方法、callable接口重写了call方法,前者无返回值,后者有返回值,返回值类型就是传进来参数值。另外callable接口还可以抛异常。 callable接口往往和...
  • 上班之余抽点时间出来写写博文,希望对新接触的朋友有帮助。今天在这里和大家一起学习一下类工厂 ...三种创立线程池的工厂方法源码: // 无界线程池 public static ExecutorService newCachedThreadPool() { ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 523
精华内容 209
关键字:

创建线程池的三种方法