Java 中的线程池

Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。
使用线程池的好处。

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

线程池的实现原理

线程池的处理流程

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

ThreadPoolExecutor执行Executor方法分下面4种情况

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(即使其他基本空闲的基本线程能够执行新任务也会创建,因为,空闲线程只会去搜索任务队列中的任务去执行;注意,执行这一步骤需要获取全局锁)。

2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。

3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。

4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。

5)特别注意, 在 corePoolSize 和 maximumPoolSize 之间的线程数会被自动释放。 当线程池中线程数量大于 corePoolSize 时, 如果某线程空闲时间超过 keepAliveTime, 线程将被终止, 直至线程池中的线程数目不大于 corePoolSize。 这样, 线程池可以动态的调整池中的线程数。

public void excute(Runnable command){
    if(commond == null)
        throw new NullPointerException();
    if(poolsize>=corePoolSize || !addIfUnderCorePoolSize(command){
        if(runstate == RUNNING && workQueue.offer(command)){//②
            if(*runstate!=RUNNING || poolSize==0)
                ensureQUeueTaskHandled(command);
        }
    else if(!addIfUnderMaximumPoolSize(command))//③
        reject(command);//④
    }
}

线程池的使用

1.创建一个线程池

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具(在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的)。

真正的线程池接口是ExecutorService继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等)。

抽象类AbstractExecutorService 实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法。(没有实现execute 方法,因而是抽象类)

ThreadPoolExecutor是线程池实现的核心类(Executor接口中定义的execute方法的作用就是执行提交的任务,该方法在抽象类AbstractExecutorService中没有实现,由该类中实现),Executors中的newFixedThreadPool等方法就是由ThreadPoolExecutor实现。

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。(如果线程池中抛出异常,内部会被try,catch掉。可以通过future.get()捕获异常)

newFixedThreadPool:创建一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化(如果某个线程由于发生了未预期Exception而结束,那么线程池会补充一个新的线程)。
newCachedThreadPool:创建一个可缓存的线程池,如果线程池当前规模超过了处理需求,那么将回收空间线程,而当需求增加时,可以添加新线程,线程池规模不存在任何限制(不推荐使用,线程池规模不可控)大小为Integer.MAX_VALUE,2的31次方-1。

newSingleThreadExecutor:是一个单线程的Executor,如果这个线程异常结束,会创建另一个线程替代,它能保证依照任务在队列中的顺序串行执行(FIFO, LIFO, 优先级)。

newScheduledThreadPool:创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

ExecutorService threadPool = Executors.newFixedThreadPool(3); 
ExecutorService threadPool = Executors.newCachedThreadPool();
ExecutorService threadPool = Executors.newSingleThreadExecutor();
threadPool.execute(Runnable r);
threadPool.shutdown();将任务执行完毕后关闭
threadPool.shutdownNow();立即关闭,并返回没有执行的任务(List<Runnable>)

上述代码也表明:接口是不能实例化的,但是接口可以声明一个引用,指向其实现类,也就是说,在实际中返回值都是这个接口的实现类的对象

2.向线程池提交任务

1)execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功.

threadpool.execute(new Runnable()
                   {
                       public void run()
                       {
                           //TODO
                       }
                   });

2)submit方法用于提交需要返回值的任务,线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取值,get方法会阻塞当前线程直到任务完成,而使用get(long timeout, TimeUnit unit) 方法则会阻塞当前线程一段时间后立即返回,这时候可能任务还没执行完。

Future<Object> future  =executor.submit(harReturnValuetask);
try
{
    Object s= future.get();
}catch(InterruptedException e)
{

}finally
{
    executor.shutdown();
}
3.关闭线程池

可以通过调用线程池的shutdown或shutdownNow方法来关闭线程池。它们的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。

shutdownNow:
1.首先将线程池的状态设置成STOP
2.停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表

shutdown:
1.将线程池的状态设置成SHUTDOWN状态
2.中断所有没有正在执行任务的线程,不再接受新的任务。

核心线程池的内部实现

对于核心的几个线程池,无论是newFixedThreadPool()方法,newSingleThreadExecutor()还是newCachedThreadPool()方法,虽然看起来创建的线程有着完全不同的特点,但是内部实现均使用了ThreadPoolExecutor 实现都是return了一个 new ThreadPoolExecutor().
核心的线程池都是ThreadPoolExecutor类的封装.

通过ThreadPoolExecutor来创建一个线程池:

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

1)corePoolSize(线程池的基本大小)

2)maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。

3)keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。

4) unit keepAlivetime的单位.

5)2)runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。(ArrayBlockingQueue, LinkedBlockingQueue, SynchronousQueue, PriorityBlockingQueue)


newFixedThreadPool 返回了一个corePoolSize和maximumPoolSize大小一样的,并且使用了LinkedBlockingQueue任务队列的线程池,

newSingleThreadExecutor()返回了一个单线程的线程池.

newCachedThreadPool()返回corePoolSize为0, maximumPoolSize无穷大的线程池, 并使用SynchronousQueue队列.


6)几种拒绝策略
拒绝策略是系统超负荷运行时的补充措施,同时,等待队列中也已经满了,塞不下新任务了.JDK提供四种拒绝策略
AbortPolicy 直接抛出异常,阻止系统正常工作
CallerRunsPolicy 只要线程池未关闭,该策略直接在调用者线程(调用execute()方法的线程)中运行当前被丢弃的任务,所以这种策略不会真的丢弃任务,但是任务提交线程的性能极有可能急剧下降.
DiscardOledestPolicy 丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务.
DiscardPolicy 默默丢弃无法处理的任务,不予以任何处理.

7)Runnable和Callable的区别
(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得,call方法有返回值,run方法没有。返回值通过Future.get()方法得到
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

合理配置线程池

性质不同的任务可以用不同规模的线程池分开处理。CPU密集型任务应该配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集性的任务,线程并不是一直在执行任务,则配置尽可能多的线程 如2*Ncpu.

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值