ThreadPoolExecutor线程池使用

线程池的作用:

  线程池作用就是限制系统中执行线程的数量。

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

为什么要用线程池:

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

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

比较重要的几个类:

ExecutorService

真正的线程池接口。

ScheduledExecutorService

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

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

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

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

 

ThreadPoolExecutor是什么

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set(核心线程数,会一直存活即使没有任务需要执行,除非设置allowCoreThreadTimeOut=true核心线程会超时关闭)
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool(线程池中最大线程数)
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.(线程空闲时间,当线程空闲时间达到keepAliveTime时,线程会退出直到数量达到corePoolSize,如果设置了allowCoreThreadTimeOut=true则数量退至0)
     * @param unit the time unit for the {@code keepAliveTime} argument(keepAliveTime的时间单位)
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.(任务执行之前保存任务的队列,队列只保存由execute提交的Runnable任务)
     * @param threadFactory the factory to use when the executor
     *        creates a new thread(线程创建工厂)
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached(任务阻塞后的拒绝策略)
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    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;
    }

 

执行顺序

  1. 当线程数小于核心线程数时,创建线程进行处理。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
  • 若线程数小于最大线程数,创建线程
  • 若线程数等于最大线程数,抛出异常,拒绝任务

初始化使用

 

Java四种线程池与ThreadPoolExecutor的关联

现有封装好的线程池:

  • newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
  • newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

 

(1)newFixedThreadPool

/**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

从以上可以看出,
  • corePoolSize与maximumPoolSize相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势;
  • keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
  • workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势;
  • FixedThreadPool的任务执行是无序的;
适用场景:可用于Web服务瞬时削峰,但需注意长时间持续高峰情况造成的队列阻塞。

 

(2)newSingleThreadExecutor

/**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * {@code newFixedThreadPool(1)} the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

从以上可以看出,
  • 单线程化的线程池,corePoolSize=maximumPoolSize=1,只有一个线程在执行,如果等待队列满了就直接拒绝执行新任务
  • 为什么还要用线程池,只能是在一般情况下它是和单线程没区别的,但是如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。这一点是单线程没办法做到的
  • 单线程线程池和定长线程池的阻塞队列使用的偶数LinkedBlockingQueue,默认的大小是int的最大值

 

(3)newCachedThreadPool

/**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

从上看出:
  • corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE,即线程数量几乎无限制;
  • keepAliveTime = 60s,线程空闲60s后自动结束。
  • workQueue 为 SynchronousQueue 同步队列,这个队列类似于一个接力棒,入队出队必须同时传递,因为CachedThreadPool线程创建无限制,不会有队列等待,所以使用SynchronousQueue;
 适合场景:
  快速处理大量耗时较短的任务,如Netty的NIO接受请求时

 

(4)newScheduledThreadPool

/**第一步
     * Creates a thread pool that can schedule commands to run after a
     * given delay, or to execute periodically.
     * @param corePoolSize the number of threads to keep in the pool,
     * even if they are idle
     * @return a newly created scheduled thread pool
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }




/**第二步
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

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


//第四步
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自定义使用

import java.util.Date;
import java.util.concurrent.*;

public class Test {
    //线程池 线程数目
    private static int ipTpsExecutorPoolCoreSize = 5;
    //线程池 最大排队数量,当请求过多时阻塞调用线程
    private static int ipTpsExecutorPoolMaxSize = 5;
    //线程池
    private static ExecutorService ipTpsExecutor;

    public static void test(final String ip) {
        ipTpsExecutor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ip);
            }
        });

    }

    public static void main(String[] args) {
        ipTpsExecutor = new ThreadPoolExecutor(ipTpsExecutorPoolCoreSize,
                ipTpsExecutorPoolMaxSize, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>());
        System.out.println("自定义ThreadPoolExecutor开始"+new Date());
        for(int i = 0;i<ipTpsExecutorPoolMaxSize+10;i++) {
            test(",ip:"+i);
        }

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
        System.out.println("newScheduledThreadPool,延迟3s开始"+new Date());
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("schedule run "+new Date());
            }
        }, 3, TimeUnit.SECONDS);


        ScheduledExecutorService es = Executors.newScheduledThreadPool(5);
        System.out.println("newScheduledThreadPool,延迟2s开始时间每三秒执行一次"+new Date());
        Runnable syncRunnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+new Date());
            }
        };
        es.scheduleAtFixedRate(syncRunnable, 2000, 3000, TimeUnit.MILLISECONDS);
    }
}

输出结果:

自定义ThreadPoolExecutor开始Wed Jan 20 14:36:54 CST 2021
pool-1-thread-2,ip:1
pool-1-thread-1,ip:0
pool-1-thread-2,ip:5
pool-1-thread-2,ip:6
pool-1-thread-1,ip:7
pool-1-thread-2,ip:8
pool-1-thread-1,ip:9
pool-1-thread-2,ip:10
pool-1-thread-1,ip:11
pool-1-thread-3,ip:2
pool-1-thread-5,ip:4
pool-1-thread-2,ip:12
pool-1-thread-3,ip:14
pool-1-thread-1,ip:13
pool-1-thread-4,ip:3
newScheduledThreadPool,延迟3s开始Wed Jan 20 14:36:54 CST 2021
newScheduledThreadPool,延迟2s开始时间每三秒执行一次Wed Jan 20 14:36:54 CST 2021
pool-3-thread-1Wed Jan 20 14:36:56 CST 2021
schedule runWed Jan 20 14:36:57 CST 2021
pool-3-thread-1Wed Jan 20 14:36:59 CST 2021
pool-3-thread-2Wed Jan 20 14:37:02 CST 2021
pool-3-thread-1Wed Jan 20 14:37:05 CST 2021
pool-3-thread-3Wed Jan 20 14:37:08 CST 2021
pool-3-thread-3Wed Jan 20 14:37:11 CST 2021
pool-3-thread-3Wed Jan 20 14:37:14 CST 2021
pool-3-thread-3Wed Jan 20 14:37:17 CST 2021
pool-3-thread-3Wed Jan 20 14:37:20 CST 2021
pool-3-thread-3Wed Jan 20 14:37:23 CST 2021
pool-3-thread-3Wed Jan 20 14:37:26 CST 2021
pool-3-thread-3Wed Jan 20 14:37:29 CST 2021
pool-3-thread-3Wed Jan 20 14:37:32 CST 2021
pool-3-thread-3Wed Jan 20 14:37:35 CST 2021
pool-3-thread-3Wed Jan 20 14:37:38 CST 2021
pool-3-thread-3Wed Jan 20 14:37:41 CST 2021
pool-3-thread-3Wed Jan 20 14:37:44 CST 2021
pool-3-thread-3Wed Jan 20 14:37:47 CST 2021
pool-3-thread-3Wed Jan 20 14:37:50 CST 2021
pool-3-thread-3Wed Jan 20 14:37:53 CST 2021
pool-3-thread-5Wed Jan 20 14:37:56 CST 2021
pool-3-thread-5Wed Jan 20 14:37:59 CST 2021
pool-3-thread-5Wed Jan 20 14:38:02 CST 2021

Process finished with exit code -1

 

posted @ 2021-01-20 14:44  时间会有答案  阅读(470)  评论(0)    收藏  举报