Java线程池ThreadPoolExecutor

45次阅读

共计 2321 个字符,预计需要花费 6 分钟才能阅读完成。

  • 创建对象仅仅在 JVM 的堆里为线程分配一块内存,而创建一个线程需要调用操作系统内核的 API,然后操作系统要为线程分配一系列的资源。所以线程是一个重量级的对象,应该避免频繁的创建和销毁。
  • 线程池是一种生产者 - 消费者模式

ThreadPoolExecutor

ThreadPoolExecutor 的构造函数比较复杂,最完备的构造有 7 个参数。

ThreadPoolExecutor(//(线程数量)线程池最小线程数。int    corePoolSize,    
    // 线程池创建的最大线程数。int    maximumPoolSize,        
   
    // 当线程池中空闲线程数量超过 corePoolSize 时,多余的线程会在多长时间内被销毁。long    keepAliveTime,
    //keepAliveTime 的单位。TimeUnit    unit,
     /**
     * 任务队列,* 被添加到线程池中,但尚未被执行的任务;* 它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;*/
    BlockingQueue<Runnable>    workQueue,
    /**
     * 线程工厂,用于创建线程,一般用默认即可。*/
    ThreadFactory    threadFactory,    
    // 自定义拒绝策略。当任务太多时如何拒绝任务。RejectedExecutionHandler    handler
    )    

参数说明补充:

handler:通过这个参数你可以自定义任务的拒绝策略。如果线程池中所有的线程都在忙碌,并且工作队列也满了(前提是工作队列是有界队列),那么此时提交任务,线程池就会拒绝接收。至于拒绝的策略,你可以通过 handler 这个参数来指定。ThreadPoolExecutor 已经提供了以下 4 种策略。

  • CallerRunsPolicy:提交任务的线程自己去执行该任务。
  • AbortPolicy:默认的拒绝策略,会 throws RejectedExecutionException
  • DiscardPolicy:直接丢弃任务,没有任何异常抛出。
  • DiscardOldestPolicy:丢弃最老的任务,其实就是把最早进入工作队列的任务丢弃,然后把新任务加入 到工作队列。

详情:https://www.cnblogs.com/dafan…

ThreadPoolExecutor 提供的 3 个 submit()方法和 1 个 FutureTask 工具类来支持获得执行任务执行结果的需求:

//    提交 Runnable 任务 
Future<?> submit(Runnable task); 
//    提交 Callable 任务 
Future<T> submit(Callable<T> task); 
//    提交 Runnable 任务及结果引⽤        
Future<T> submit(Runnable task,    T result);
  1. 提交 Runnable 任务 submit(Runnable task):这个方法的参数是一个 Runnable 接口,Runnable 接扣的 run()方法是没有返回值的,所以 submit(Runnable task) 这个方法返回的 Future 仅可以用来断言任务已经结束了,类似于 Thread.join()。
  2. 提交 Callable 任务 submit(Callable<T> task):这个方法的参数是一个 Callable 接口,它只有一个 call()方法,并且这个方法是有返回值的,所以这个方法返回的 Future 对象可以通过调用其 get()方法来获取任务的执行结果。
  3. 提交 Runnable 任务及结果引用 submit(Runnable task, T result):这个方法很有意思,假设这个方法返回的 Future 对象是 f,f.get()的返回值就是传给 submit()方法的参数 result。Runnable 接口的实现类 Task 声明了一个有 参构造函数 Task(Result r),创建 Task 对象的时候传入了 result 对象,这样就能在类 Task 的 run()方法 中对 result 进行各种操作了。result 相当于主线程和子线程之间的桥梁,通过它主子线程可以共享数据。

使用并发工具包 Executors 创建线程池(不建议使用)

// 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。ExecutorService executor1 = Executors.newCachedThreadPool();

// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。ExecutorService executor2 = Executors.newFixedThreadPool(10);

// 创建一个定长线程池,支持定时及周期性任务执行。ExecutorService executor3 = Executors.newScheduledThreadPool (10);

// 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序 (FIFO, LIFO, 优先级) 执行。ExecutorService executor4 = Executors.newSingleThreadExecutor ();

不建议使用 Executors,Executors 提供的很多方法默认使用都是无界的 LinkedBlockingQueue, 高负载情境下, 无界队列很容易导致 OOM, 而 OOM 会导致所有请求都无法处理, 所以强力建议使用有界队列。

使用线程池要注意什么

无界队列很容易导致 OOM, 而 OOM 会导致所有请求都无法处理, 所以强力建议使用有界队列。

使用有界队列,当任务过多时,线程池会触发执行拒绝策略,线程默认的拒绝会。


码字不易如果对你有帮助请给个关注

爱技术爱生活 QQ 群: 894109590

正文完
 0