线程池总结

来源:互联网 发布:网络高清图片 编辑:程序博客网 时间:2024/06/06 05:33

线程池实现原理

当提交一个新的任务之后,线程池的处理如下:    1)判断核心线程池是否已经满了,如果没有满,则创建一个新的工作线程来执行任务。如果核心线程池都在执行任务,则进入下一个步骤    2)线程池判断工作队列是否已经满了。如果工作队列没有满,则将新提交的任务提交到这个工作队列中,若工作队列满了,则进入下一个流程。    3)线程池判断线程池是否都处于工作状态。如果没有,则创建一个新的线程来执行这个任务。如果已经满了,则交给饱和策略来处理这个任务。

工作图
这里写图片描述

ThreadPoolExecutor执行execute方法分下面四种情况

1)如果当前运行运行的线程少于corePoolSize,则创建新的线程来执行任务
2)如果运行的线程等于或者多余corePoolsize,则将任务加入BlockingQueue。
3)如果无法将任务加入BlockingQueue(队列已经满了),则创建新的线程来处理任务。
4)如果创建新的线程使得当前运行的线程超出maximumPoolSize,任务将被拒绝。

在ThreadPoolExecutor完成预热之后,几乎所有的方法都是执行步骤2,而步骤2不需要获取全局锁。

源码分析

public void execute(Runnable command){    if(command == null)        throw new NullPointException();    //如果线程数小于基本线程数,则创建线程并且执行    if(poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)){        //如果线程数大于等于基本线程数或者线程数创建失败,则将当前任务放在工作队列中        if(runState == RUNNING && workQueue.offer(command)){            if(runState != RUNNING || poolSize == 0)                ensureQueuedTaskHandled(command);        }else if(!addIfUnderCorePoolSize(command)){            reject(command);        }    }}

工作线程:线程池创建线程时候,会将线程封装成工作线程Worker,Worker在执行完任务后,还会循环获取工作队列里的任务来执行。

public void run(){    try{        Runnable task = firstTask;        firstTask = null;        while(task != null ||(task = getTask()) != null){            runTask(task);            task = null;        }    }finally{        workDone(this);    }}

线程池执行线程分为两种情况:
1)在execute()方法创建一个线程时候,会让这个线程执行当前任务
2)创建的线程执行完任务后,会反复从BlockingQueue获取任务执行

线程池的使用

线程池的创建

我们可以通过ThreadPoolExecutor(corePoolsize, maximunPoolSize, keepAliveTime, millseconds, runnableTaskQueue, hadnler)
corePoolSize:当提交一个任务到线程池时候,线程池会创建一个线程执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的线程数线程池的基本大小的时候就不在创建。
RunnableTaskQueue:任务队列,用于保存等待执行的任务的阻塞队列。可以选择一下几个阻塞队列:

ArrayBlokingQueue:是一个基于数组的有界的阻塞队列,此队列按照先进先出原则对元素进行排序。LinkedBlockingQueue:一个基于链表结构的阻塞队列。Executors.newFixedThreadPool()使用了这个队列。SynchronousQueue:一个不存储元素的阻塞队列。每个的插入操作必须等待另一个线程的移除操作,否在插入操作一直处于阻塞状态。静态方法Executors.newCachedThreadPool使用了这个队列

maximunPoolSize:线程池允许创建的最大线程数。如果队列满了,并且已经创建的线程数小于最大线程数,则继续创建线程执行任务。
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采用·一种策略来处理,JDK使用以下几种策略:

AbortPolicy:直接抛出异常
CallerRunsPolicy:只用调用者所在的线程来执行人物
DiscardOldestPolicy:丢弃队列里的最近的一个任务,来执行当前任务
DiscardPolicy:不处理,丢弃。

KeepAlive:线程池工作线程空闲后,保持存活的时间。
TimeUnit:存活时间单位

向线程池提交任务

可以使用两个方法来向线程池提交任务,分别是execute()和submit()方法。
execute()用来提交不需要返回值的方法

threadPool.execute(new Runnable(){    public void run(){        //....    }});
submit()用来提交需要返回值的任务。线程池返回一个Future对象,通过这个Future对象可以判断任务是否执行成功,可以通过Future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,使用get(long timeout, TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
Future<Object> future = excutor.submit(...);Object s = future.get();

关闭线程池

可以通过调用线程池的shutdown或者shutdowNow方法来关闭线程池。他们的原理是遍历线程池中的每个线程,调用线程的interrupt方法来中断线程。shutdownNow首先将线程池状态设置为stop,然后尝试终止正在执行或者或者暂停任务的状态,并且返回等待任务的列表,不能确保任务执行完。shutdown:将线程池状态设置为shutdown状态,然后中断所有没有正在执行任务的线程,确保任务执行完。
原创粉丝点击