java线程池之ThreadPoolExecutor

Posted 红桃xin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程池之ThreadPoolExecutor相关的知识,希望对你有一定的参考价值。

why

为什么你需要线程池?答案往往是当你开发个简单并发的java程序,或者创建Runnable对象,用Thread来执行。创建一个java线程是个非常昂贵的操作。如果每次创建新的线程实例执行Task, 应用表现是糟糕的。

如何创建线程池

一个线程池是一组预先定义的线程集合。一般来说,池的大小是固定的,但是不是强制的。这方便用相同的线程执行多个任务。如果有任务大于池的大小,需要在队列里等待FIFO

ThreadPoolExecutor

自从java5, java并发api提供Executor framework机制。实现ExecutorExecutorService
它分隔任务创建和执行。你必须实现Runnable接口,然后用executor执行。当你发送任务到executor,它会努力使用线程池执行此任务,去避免连续新增线程。

怎么使用

  1. 固定线程池 - 创建线程池复用固定数量的线程执行任何数量的任务。当所有线程在执行状态,再提交额外的任务,他们等待在队列中直到线程空闲。大部分的最佳实现

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

2.缓存线程池 - 创建线程池尽可能需要的。但是也会复用先前构建的空闲线程。如果有耗时任务,不要使用此线程池。当线程数超过系统可以处理的数目,这会拖垮整个系统。

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();

3.计划线程池- 设定给定的延迟或者执行周期的计划命令

ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newScheduledThreadPool(10);

  1. 单线程池 - 创建单线程执行所有任务。当只有一个任务时使用。

ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newSingleThreadExecutor();

  1. 任务窃取线程池 - 创建一个线程池维护足够线程去支持足够量的并发量。换句话,在多处理器机器的同时时间点用最大数量的线程执行给定的任务。

ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newWorkStealingPool(4);

ThreadPoolExecutor 例子

  1. 创建task - 创建一个任务 [每次使用随机时间完成]
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolExample 
{
    public static void main(String[] args) 
    {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);      
        for (int i = 1; i <= 5; i++) 
        {
            Task task = new Task("Task " + i);
            System.out.println("Created : " + task.getName());
            executor.execute(task);
        }
        executor.shutdown();
    }
}
  1. 使用线程池执行任务
    创建5个任务和创建执行队列。这个执行器有二个线程去执行所有任务。
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolExample 
{
    public static void main(String[] args) 
    {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
        for (int i = 1; i <= 5; i++) 
        {
            Task task = new Task("Task " + i);
            System.out.println("Created : " + task.getName());
            executor.execute(task);
        }
        executor.shutdown();
    }
}

Program output:

Created : Task 1
Created : Task 2
Created : Task 3
Created : Task 4
Created : Task 5
Executing : Task 1
Executing : Task 2
Executing : Task 3
Executing : Task 4
Executing : Task 5

总结

  1. ThreadPoolExecutor 类有四个不同构造方法,但是由于他们复杂性,java目前的api提供 Executors使用线程池。相对直接创建线程池,比较推荐使用Executors类。
  2. 缓存池 the chached thread pool 好处是方便,坏处是如果超过系统处理能力,使系统载。可以用固定线程池解决,下个博客再说吧。
  3. 关键层面,你必须显式结束。要不然,executor会继续执行,程序不会结束。
  4. 可以使用**ThreadPoolExecutor.shutdown()关闭,当完成所有在执行任务。调用shutdown()**后,你再发送其它任务,它会拒绝,然后throw 异常RejectedExecutionException
  5. 提供很多方法获取线程池相关的信息和状态。例如:getPoolSize() , getActiveCount(), getCompletedTaskCount() , getLargestPoolSize()
  6. 结束执行器有下面方法:
  • shutdownNow(): 立即关闭executor. 返回pending任务list。正在running的任务不会因为这个方法调用结束,而是继续执行。
  • isTerminated():如果执行 shutdown() 或者 shutdownNow() 方法并且完成关闭,就会返回true
  • isShutdown(): 如果 执行shutdown() 返回true
  • awaitTermination(long timeout, TimeUnit unit): 此方法blocks the calling thread , 等到执行器结束或者超时发生. TimeUnit是枚举类 DAYS,HOURS,MICROSECONDS ETC

以上是关于java线程池之ThreadPoolExecutor的主要内容,如果未能解决你的问题,请参考以下文章

java线程池之ThreadPoolExecutor

java线程池之newSingleThreadExecutor

Java线程池之ForkJoinPool

java线程池之newCachedThreadPool

java线程池之newFixedThreadPool

Java 线程池之FixedThreadPool(Java代码实战-003)