java 线程池

作者: jijs | 来源:发表于2017-10-31 22:43 被阅读233次

    概述

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

    java 中提供了3种实现

    1. ThreadPoolExecutor:标准线程池
    2. ScheduledThreadPoolExecutor:支持延迟任务的线程池
    3. ForkJoinPool:类似于ThreadPoolExecutor,但是使用work-stealing模式,其会为线程池中的每个线程创建一个队列,从而用work-stealing(任务窃取)算法使得线程可以从其它线程队列里窃取任务来执行。既如果自己的任务处理完成了,则可以去忙碌的工作线程那里窃取任务执行。

    1.ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler){
     ...... 
    }
    
    • corePoolSize:核心线程池大小,线程池维护的线程最少线程。
    • maximumPoolSize:线程池最大大小。
    • keepAliveTime:线程池中线程的最大空闲时间,如果线程池中的线程个数大于corePoolSize,并且线程空闲时间超过该时间的线程会被回收。
    • unit: 线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
    • workQueue: 线程池中存放工作任务的队列。常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue
    • threadFactory: 线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法,通过线程工厂可以对线程的一些属性进行定制。
    • handler:线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy()。

    Executors 提供了3个工厂方法创建 ThreadPoolExecutor

    1. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
    }
    
    1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
    }
    
    1. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
    }
    

    2. ScheduledThreadPoolExecutor

    我们通过查看ScheduledThreadPoolExecutor的源代码,可以发现ScheduledThreadPoolExecutor继承了ThreadPoolExecutor。

    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }
    

    从构造中发现ScheduledThreadPoolExecutor线程池其实是调用了ThreadPoolExecutor构造进行实现的,只是ThreadPoolExecutor使用的工作队列是java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue通过名字我们都可以猜到这个是一个延时工作队列。
    因为ScheduledThreadPoolExecutor的最大线程是Integer.MAX_VALUE,而且根据源码可以看到execute和submit其实都是调用schedule这个方法,而且延时时间都是指定为0,所以调用execute和submit的任务都直接被执行。

    Executors 也提供了ScheduledThreadPoolExecutor创建方式

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    

    ScheduledThreadPoolExecutor 提供了3种延迟执行策略

    1. schedule(commod,delay,unit) ,这个方法是说系统启动后,需要等待多久执行,delay是等待时间。只执行一次,没有周期性。

    2. scheduleAtFixedRate(commod,initialDelay,period,unit),固定速率执行周期任务。这个是以period为固定周期时间,按照一定频率来重复执行任务,initialDelay是说系统启动后,需要等待多久才开始执行。

    3. scheduleWithFixedDelay(commod,initialDelay,delay,unit),固定延迟执行周期任务。这个是以delay为固定延迟时间,按照一定的等待时间来执行任务,initialDelay意义与上面的相同。

    3. ForkJoinPool

    在Java 7中引入了一种新的线程池:ForkJoinPool。
    它同ThreadPoolExecutor一样,也实现了Executor和ExecutorService接口。它使用了一个无限队列来保存需要执行的任务,而线程的数量则是通过构造函数传入,如果没有向构造函数中传入希望的线程数量,那么当前计算机可用的CPU数量会被设置为线程数量作为默认值。

    Executors 提供了 ForkJoinPool 的创建方式

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
    

    并发数默认为当前系统cpu个数。

    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
            ForkJoinPool.defaultForkJoinWorkerThreadFactory,
            null, true);
    }
    

    也可以指定并行数。

    想了解更多精彩内容请关注我的公众号

    相关文章

      网友评论

        本文标题:java 线程池

        本文链接:https://www.haomeiwen.com/subject/hhzmpxtx.html