• java中线程池的使用


    线程池的作用:
    线程池作用就是限制系统中执行线程的数量。
         根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排 队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。

    为什么要用线程池:
    1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
    2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    Executor框架
    Executor框架在Java1.5中引入,大部分的类都在包java.util.concurrent中,由大神Doug Lea写成,其中常用到的有以下几个类和接口:
    java.util.concurrent.Executor一个只包含一个方法的接口,它的抽象含义是:用来执行一个Runnable任务的执行器。
    java.util.concurrent.ExecutorService对Executor的一个扩展,增加了很多对于任务和执行器的生命周期进行管理的接口,也是通常进行多线程开发最常使用的接口。
    java.util.concurrent.ThreadFactory一个生成新线程的接口。用户可以通过实现这个接口管理对线程池中生成线程的逻辑
    java.util.concurrent.Executors提供了很多不同的生成执行器的实用方法,比如基于线程池的执行器的实现。


    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

    比较重要的几个类:

    ExecutorService

    真正的线程池接口。

    ScheduledExecutorService

    能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

    ThreadPoolExecutor

    ExecutorService的默认实现。

    ScheduledThreadPoolExecutor

    继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

    Executor框架中最常用的就是java.util.concurrent.ThreadPoolExecutor:
    对于它的描述:它维护了一个线程池,对于提交到此Executor中的任务,它不是创建新的线程而是使用池内的线程进行执行。
    对于数量巨大但执行时间很小的任务,可以显著地减少对于任务执行的开销。

    java.util.concurrent.ThreadPoolExecutor的使用

    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,RejectedExecutionHandler handler)

    创建一个线程池需要的几个参数:
    1.corePoolSize:
    线程池的基本大小,当提交一个任务到线程池,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于corePoolSize时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
    2.maximumPoolSize:
    线程池最大大小,线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
    3.keepAliveTime:
    线程活动保持时间,线程池的工作线程空闲后,保持存活的时间。
    4.unit:
    参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

    TimeUnit.DAYS;               //天
    TimeUnit.HOURS;             //小时
    TimeUnit.MINUTES;           //分钟
    TimeUnit.SECONDS;           //秒
    TimeUnit.MILLISECONDS;      //毫秒
    TimeUnit.MICROSECONDS;      //微妙
    TimeUnit.NANOSECONDS;       //纳秒
    5.workQueue:
    任务队列,用于保存等待执行的任务的阻塞队列,可以选择以下几个队列:
    a、ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    b、LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
    c、SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
    d、PriorityBlockingQueue:一个具有优先级得无限阻塞队列。
    6.threadFactory:
    用于设置创建线程的工厂,可以通过线程工程给每个创建出来的线程设置更有意义的名字。
    7.handler:
    饱和策略,当线程池和队列都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。

    在ThreadPoolExecutor类中有几个非常重要的方法:
    execute()
    submit()
    shutdown()
    shutdownNow()

    execute()方法:
    实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
    submit()方法:
    是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。
    shutdown()和shutdownNow()是用来关闭线程池的。

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

    Java通过Executors提供四种线程池方法:
    1.newCachedThreadPool:

    创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    这种类型的线程池特点是:
    工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
    如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。
    在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
    2.newFixedThreadPool:

    创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
    FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
    3.newScheduledThreadPool:

    创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。
    4.newSingleThreadExecutor:

    创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

    实例代码

    1.newCachedThreadPool示例:

    package com.example.threadpool;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class NewCachedThreadPool {
        public static void main(String[] args) {
            ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {  
                final int index = i;  
                try {  
                    Thread.sleep(index * 1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                cachedThreadPool.execute(new Runnable() {  
                    public void run() {  
                        System.out.println(index);  
                    }  
                });  
            }
        }

    }
    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

    2.newFixedThreadPool示例:

    package com.example.threadpool;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class NewFixedThreadPool {

        public static void main(String[] args) {
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
            for (int i = 0; i < 10; i++) {
                final int index = i;
                fixedThreadPool.execute(new Runnable() {    
                    @Override
                    public void run() {
                        try {
                            System.out.println(index);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    }

    因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。

    定长线程池的大小最好根据系统资源进行设置。

    3.newScheduledThreadPool示例:

    表示延迟3秒执行:

    package com.example.threadpool;

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    public class NewScheduleThreadPool {
        public static void main(String[] args) {
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
              scheduledThreadPool.schedule(new Runnable() {
               public void run() {
                System.out.println("delay 3 seconds");
               }
              }, 3, TimeUnit.SECONDS);
        }
    }

    表示延迟1秒后每3秒执行一次:

    package com.example.threadpool;

    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;

    public class NewScheduleThreadPool {
        public static void main(String[] args) {
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("delay 1 seconds, and excute every 3 seconds");
                }
            }, 1, 3, TimeUnit.SECONDS);
        }
    }

    4.newSingleThreadExecutor示例:

    package com.example.threadpool;

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;

    public class NewSingleThreadExecutor {
        public static void main(String[] args) {
            ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 10; i++) {
                final int index = i;
                singleThreadExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            System.out.println(index);
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
    结果依次输出,相当于顺序执行各个任务。

  • 相关阅读:
    20190211 模拟训练 A. 大猫咪
    如何诊断节点重启问题
    诊断 Grid Infrastructure 启动问题 (文档 ID 1623340.1)
    bzoj4025 二分图
    root.sh脚本支持checkpoints文件实现重复运行
    [IOI2018] seats 排座位
    最常见的 5 个导致节点重新启动、驱逐或 CRS 意外重启的问题 (文档 ID 1524455.1)
    [IOI2018] werewolf 狼人
    OCR/Vote disk 维护操作: (添加/删除/替换/移动) (文档 ID 1674859.1)
    [POI2011]ROT-Tree Rotations
  • 原文地址:https://www.cnblogs.com/web424/p/6861945.html
Copyright © 2020-2023  润新知