Java线程池

来源:互联网 发布:适合美工的笔记本 编辑:程序博客网 时间:2024/06/08 06:27

一、原理

1.1 应用场景

1.1.1 为什么要使用线程池?

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

总体来说,是为了提高系统的整体性能。

1.1.2 使用场景

首先,线程的生命周期及使用时间包括:创建(t1)、 运行(t2)、 销毁(t3),即线程运行的总时间

T =  t1 + t2 + t3

假如,有些业务逻辑需要频繁的使用线程执行某些简单的任务,那么很多时间都会浪费t1和t3上(创建和销毁),为了避免不必要的时间开销,可以采用线程池,在线程池中的线程可以复用,当线程运行完任务之后,不被销毁。

1.2 原理实现

结合以下两图,两图中1、2、3、4即是线程池的工作原理,

1、首先线程池判断基本线程池是否已满,如果没满,创建一个工作线程来执行任务。满了,则进入下个流程。
2、其次线程池判断工作队列是否已满,如果没满,则将新提交的任务存储在工作队列里。满了,则进入下个流程。
3、最后线程池判断整个线程池是否已满,如果没满,则创建一个新的工作线程来执行任务。
4、如果整个线程池已经满了,则交给饱和策略来处理这个任务。


二、实现

上述工作原理即是Java线程池的工作原理,同时Java中 java.util.concurrent 包中提供了线程池的实现,即Executor框架,其基本结构图如下图所示:

包括三个部分:

(1)Executor接口

Executor接口是Executor框架中最基础的部分,定义了一个用于执行Runnable的execute方法,它没有实现类只有另一个重要的子接口ExecutorService

public interface Executor {      void execute(Runnable command); }

(2)ExecutorService接口

ExecutorService接口继承自Executor接口,定义了终止、提交、执行任务、跟踪任务返回结果等方法

execute(Runnable command):执行Ruannable类型的任务submit(task):可用来提交Callable或Runnable任务,并返回代表此任务的Future对象shutdown():在完成已提交的任务后关闭线程池,不再接管新任务,shutdownNow():停止所有正在执行的任务并关闭线程池。isTerminated():测试是否所有任务都执行完毕了。isShutdown():测试是否该ExecutorService已被关闭

(3)Executors的静态方法

负责生成各种类型的ExecutorService线程池实例,

newFixedThreadPool(numberOfThreads:int):(固定线程池)ExecutorService 创建一个固定线程数量的线程池,并行执行的线程数量不变,线程当前任务完成后,可以被重用执行另一个任务newCachedThreadPool():(可缓存线程池)ExecutorService 创建一个线程池,按需创建新线程,就是有任务时才创建,空闲线程保存60s,当前面创建的线程可用时,则重用它们newSingleThreadExecutor():(单线程执行器)线程池中只有一个线程,依次执行任务newScheduledThreadPool():线程池按时间计划来执行任务,允许用户设定执行任务的时间newSingleThreadScheduledExcutor():线程池中只有一个线程,它按规定时间来执行任务

以下代码是三个例子的实现:

public static ExecutorService newFixedThreadPool(int nThreads) {     return new ThreadPoolExecutor(nThreads, nThreads,                                   0L, TimeUnit.MILLISECONDS,                                   new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newSingleThreadExecutor() {     return new FinalizableDelegatedExecutorService         (new ThreadPoolExecutor(1, 1,                                 0L, TimeUnit.MILLISECONDS,                                 new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newCachedThreadPool() {     return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                   60L, TimeUnit.SECONDS,                                   new SynchronousQueue<Runnable>()); }

上述几个实例都是调用了 ThreadPoolExecutor 类,可根据不同的参数定制不同类型的线程池,

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);

其中重要的参数:

private final BlockingQueue<Runnable> workQueue;              //任务缓存队列,用来存放等待执行的任务private final ReentrantLock mainLock = new ReentrantLock();   //线程池的主要状态锁,对线程池状态(比如线程池大小                                                              //、runState等)的改变都要使用这个锁private final HashSet<Worker> workers = new HashSet<Worker>();  //用来存放工作集  private volatile long  keepAliveTime;    //线程存货时间  private volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间private volatile int   corePoolSize;     //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)private volatile int   maximumPoolSize;   //线程池最大能容忍的线程数  private volatile int   poolSize;       //线程池中当前的线程数private volatile RejectedExecutionHandler handler; //任务拒绝策略private volatile ThreadFactory threadFactory;   //线程工厂,用来创建线程private int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数private long completedTaskCount;   //用来记录已经执行完毕的任务个数

三、示例

以下是参考示例:设置了一个初始为5,最大容量是10的线程池,开启15个线程开始运行。

public class Test {    public static void main(String[] args) {        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,                new ArrayBlockingQueue<Runnable>(5));         for(int i=0;i<15;i++){            MyTask myTask = new MyTask(i);            executor.execute(myTask);            System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+                    executor.getQueue().size()+",已执行完的任务数目:"+executor.getCompletedTaskCount());        }        executor.shutdown();    }}  class MyTask implements Runnable {    private int taskNum;     public MyTask(int num) {        this.taskNum = num;    }     @Override    public void run() {        System.out.println("正在执行task "+taskNum);        try {            Thread.currentThread().sleep(4000);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("task "+taskNum+"执行完毕");    }}

输出结果:
正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完的任务数目:0
正在执行task 1
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完的任务数目:0
正在执行task 2
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完的任务数目:0
正在执行task 4
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行完的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:6,队列中等待执行的任务数目:5,已执行完的任务数目:0
正在执行task 10
线程池中线程数目:7,队列中等待执行的任务数目:5,已执行完的任务数目:0
正在执行task 11
线程池中线程数目:8,队列中等待执行的任务数目:5,已执行完的任务数目:0
线程池中线程数目:9,队列中等待执行的任务数目:5,已执行完的任务数目:0
正在执行task 12
正在执行task 13
线程池中线程数目:10,队列中等待执行的任务数目:5,已执行完的任务数目:0
正在执行task 14
task 4执行完毕
task 10执行完毕
正在执行task 5
正在执行task 6
task 1执行完毕
正在执行task 7
task 0执行完毕
正在执行task 8
task 3执行完毕
正在执行task 9
task 2执行完毕
task 11执行完毕
task 12执行完毕
task 13执行完毕
task 14执行完毕
task 7执行完毕
task 5执行完毕
task 6执行完毕
task 8执行完毕
task 9执行完毕

 



原创粉丝点击