zoukankan      html  css  js  c++  java
  • java (ThreadPoolExecutor)线程池的使用

    如果我们在主线程中新起一百个线程,让这一百个线程同时工作,逻辑上是没有任何问题的,但是这样做对系统资源的开销很大,这样会在短时间内处理很多的任务,当然包括新起线程等等。基于这样的考虑,我们是有必要引入线程池这个东西的。线程池就是一个池子,池子里有很多可用线程资源,如果需要就直接从这个池子里拿就是。当不用的时候,放入池子中,线程池会自动帮我们管理。所以使用线程池主要有以下两个好处:1、减少在创建和销毁线程上所花的时间以及系统资源的开销 2、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存 。

        如果我们想要使用线程池,就需要先定义这个线程池。定义线程池的时候,其中的几个主要参数说明如下:
    -corePoolSize(int):线程池中保持的线程数量,包括空闲线程在内。也就是线程池释放的最小线程数量界限。

    -maximumPoolSize(int):线程池中嫩容纳最大线程数量。

    -keepAliveTime(long):空闲线程保持在线程池中的时间,当线程池中线程数量大于corePoolSize的时候。

    -unit(TimeUnit枚举类):上面参数时间的单位,可以是分钟,秒,毫秒等等。

    -workQueue(BlockingQueue<Runnable>):任务队列,当线程任务提交到线程池以后,首先放入队列中,然后线程池按照该任务队列依次执行相应的任务。可以使用的workQueue有很多,比如:LinkedBlockingQueue等等。

    -threadFactory(ThreadFactory类):新线程产生工厂类。

    -handler(RejectedExecutionHandler类):当提交线程拒绝执行、异常的时候,处理异常的类。该类取值如下:(注意都是内部类)

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务,重复此过程。
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

       除了自定义线程池以外, java提供了几种常用的线程池,可以快捷的供程序员使用,他们分别是:

    1、newFixedThreadPool 创建固定大小数量线程池,数量通过传入的参数决定。

    2、newSingleThreadExecutor 创建一个线程容量的线程池,所有的线程依次执行,相当于创建固定数量为1的线程池。

    3、newCachedThreadPool 创建可缓存的线程池,没有最大线程限制(实际上是Integer.MAX_VALUE)。如果用空闲线程等待时间超过一分钟,就关闭该线程。

    4、newScheduledThreadPool 创建计划(延迟)任务线程池,线程池中的线程可以让其在特定的延迟时间之后执行,也可以以固定的时间重复执行(周期性执行)。相当于以前的Timer类的使用。
    5、newSingleThreadScheduledExecutor 创建单线程池延迟任务,创建一个线程容量的计划任务。

        其实通过静态方法创建的上面几种线程池,也都是通过传入默认的各个参数,然后返回一个有各自特点的线程池。具体参数可以通过查看jdk源码阅读。

        有了线程池,那么我们如何利用线程池中线程执行我们的任务,由于Java将线程池的封装,我们拿到的线程池的线程其实是一个包含线程任务的执行器,只需要调运执行器的执行方法,就会自动执行我们线程中的任务。对于非计划任务,我们需要拿到一个ThreadPoolExecutor,对于计划任务,我们需要拿到一个ScheduledThreadPoolExecutor(它是ThreadPoolExecutor的子类)。在了解这两个类之前,需要先了解两个接口,ExecutorService以及它的子接口ScheduleThreadExecutorService接口,上面两个接口分别实现了这两个接口,这个两接口定义了execute(Runnable r)方法,这个方法去执行线程的任务。也就是我们通过调运ThreadPoolExecutor或者ScheduledThreadPoolExecutor的execute(Runnable r)方法开启我们的线程,并且执行我们的线程任务,具体代码如下:

     定义一个单例的线程池:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public class MyPool
    {
        private static MyPool myPool = null;
        //单例线程池中有两种具体的线程池
        private ThreadPoolExecutor threadPool = null;
        private ScheduledThreadPoolExecutor scheduledPool = null;
     
        public ThreadPoolExecutor getThreadPool()
        {
            return threadPool;
        }
     
        public ScheduledThreadPoolExecutor getScheduledPool()
        {
            return scheduledPool;
        }
     
        //设置线程池的各个参数的大小
        private int corePoolSize = 10;// 池中所保存的线程数,包括空闲线程。
        private int maximumPoolSize = 20;// 池中允许的最大线程数。
        private long keepAliveTime = 3;// 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
        private int scheduledPoolSize = 10;
     
        private static synchronized void create()
        {
            if (myPool == null)
                myPool = new MyPool();
        }
     
        public static MyPool getInstance()
        {
            if (myPool == null)
                create();
            return myPool;
        }
     
        private MyPool()
        {
            //实例化线程池,这里使用的LinkedBlockingQueue作为workQueue,使用DiscardOldestPolicy作为handler
            this.threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                    keepAliveTime, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(),
                    new ThreadPoolExecutor.DiscardOldestPolicy());
            //实例化计划任务线程池
            this.scheduledPool = new ScheduledThreadPoolExecutor(scheduledPoolSize);
        }
    }

        获取线程池中的线程,并且执行线程中任务:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Test
    public void testThreadPool()
    {
     
        ThreadPoolExecutor pool1 = (ThreadPoolExecutor) Executors
                .newCachedThreadPool();
        pool1.execute(new Runnable()
        {
     
            @Override
            public void run()
            {
                System.out.println("快捷线程池中的线程!");
            }
        });
     
        ThreadPoolExecutor pool2 = MyPool.getInstance().getThreadPool();
        pool2.execute(new Runnable()
        {
     
            @Override
            public void run()
            {
                System.out.println("普通线程池中的线程");
            }
        });
     
        ScheduledThreadPoolExecutor pool3 = MyPool.getInstance()
                .getScheduledPool();
        pool3.scheduleAtFixedRate(new Runnable()
        {
     
            @Override
            public void run()
            {
                System.out.println("计划任务线程池中的线程");
            }
        }, 0, 1000, TimeUnit.MILLISECONDS);
    }

    四、计划任务执行使用

        通过上面的例子,也看到了计划任务线程池的使用方式。对于计划任务,除了可以执行普不同线程池中线程的任务以外,还可以执行计划任务特殊的线程要求,比如:scheduleWithFixedDelay(command, initialDelay, delay, unit);在初始化延迟之后,以特定的延迟时间重复执行。scheduleAtFixedRate(command, initialDelay, period, unit);在初始化延迟时间之后,以固定频率重复执行。这两种的区别是下一次执行时间延迟计算的开始时间不同,第一种是从上一次任务开始执行的时候计算,第二种是从上一次任务执行结束的时候计算。这两种和java之前版本中Timer类很相似。但是Timer有很多缺陷,具体的缺陷不再详细说明。而这些缺陷都可以通过ScheduledExecutorService给完美解决。所以是时候该丢弃Timer了。

  • 相关阅读:
    学习Java的Day02
    学习Java的Day01
    多线程的了解
    几个MQ的区别
    HTML5——存储(cookie、localStorage、sessionStorage)的区别
    dubbo mock配置
    Springboot分布式,excel导出,运用POI导出,前端用的jsp
    oracle 添加字段和添加注释
    可以重复的Map:IdentityHashMap
    数组转list问题
  • 原文地址:https://www.cnblogs.com/niudaxianren/p/12718205.html
Copyright © 2011-2022 走看看