Java Executor并发框架(一)整体介绍

一、概述

Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为java提供了线程池。在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在JDK1.5后,有了很大的改善。JDK1.5之后加入了java.util.concurrent包,java.util.concurrent包的加入给予开发人员开发并发程序以及解决并发问题很大的帮助。这篇文章主要介绍下并发包下的Executor接口,Executor接口虽然作为一个非常旧的接口(JDK1.5 2004年发布),但是很多程序员对于其中的一些原理还是不熟悉,因此写这篇文章来介绍下Executor接口,同时巩固下自己的知识。如果文章中有出现错误,欢迎大家指出。

二、Executors工厂类

对于数据库连接,我们经常听到数据库连接池这个概念。因为建立数据库连接是非常耗时的一个操作,其中涉及到网络IO的一些操作。因此就想出把连接通过一个连接池来管理。需要连接的话,就从连接池里取一个。当使用完了,就“关闭”连接,这不是正在意义上的关闭,只是把连接放回到我们的池里,供其他人在使用。所以对于线程,也有了线程池这个概念,其中的原理和数据库连接池是差不多的,因此java jdk中也提供了线程池的功能。

线程池的作用:线程池就是限制系统中使用线程的数量以及更好的使用线程。

    根据系统的运行情况,可以自动或手动设置线程数量,达到运行的最佳效果:配置少了,将影响系统的执行效率,配置多了,又会浪费系统的资源。用线程池配置数量,其他线程排队等候。当一个任务执行完毕后,就从队列中取一个新任务运行,如果没有新任务,那么这个线程将等待。如果来了一个新任务,但是没有空闲线程的话,那么把任务加入到等待队列中。

为什么要用线程池:

  1. 减少线程创建和销毁的次数,使线程可以多次复用。
  2. 可以根据系统情况,调整线程的数量。防止创建过多的线程,消耗过多的内存(每个线程1M左右)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行任务的工具。真正的线程池接口是ExecutorService。Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。

比较重要的几个类:

ExecutorService真正的线程池接口
ScheduledExecutorService能和Timer/TimerTask类似,解决那些需要任务重复执行的问题
ThreadPoolExecutorExecutorService的默认实现
ScheduledThreadPoolExecutor周期性任务调度

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

三、Executor任务提交接口和Executors工具类

Executor框架在Java 5中被引入。Executor框架是一个根据一组执行策略调度异步执行任务的框架。Executor存在的目的是提供一种将"任务提交"与"任务如何运行"分离开来的机制。定义如下:


虽然只有一个方法,但是却为我们的异步任务执行框架提供了基础。它提供了一种标准的方法将任务的提交过程与执行过程解耦开来,并用Runnable接口来表示将要执行的任务。那么我们怎么得到Executor对象呢?这就是接下来要介绍的Executors了。

Executors为Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable类提供了一些工具方法,类似于集合中的Collections类的功能。Executors可以方便的被用来创建线程池。每个线程池都有相应的使用场景,我们需要牢记,这在面试中是很有用的。

(1). newCachedThreadPool :该线程池比较适合没有固定大小并且比较快速就能完成的小任务,它将为每个任务创建一个线程。那这样子它与直接创建线程对象(new Thread())有什么区别呢?看到它的第三个参数60L和第四个参数TimeUnit.SECONDS了吗?好处就在于60秒内能够重用已创建好的线程如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒处于等待任务到来)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池的最大值是Integer的最大值(2^31-1)。下面是Executors中的newCachedThreadPool()的源代码:


(2). newFixedThreadPool创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,再提交新任务,任务将会进入等待队列中等待。如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。下面Executors中的newFixedThreadPool()的源代码:


(3). newSingleThreadExecutor创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代, 它所有的任务将会使用相同的线程。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。下面是Executors中的newSingleThreadExecutor()的源代码: 



通过如上配置的线程池的创建方法源代码,我们可以发现:

1. 除了CachedThreadPool使用的是直接提交策略的缓冲队列以外,其余两个采用的都是无界缓冲队列,也就说,FixedThreadPool和SingleThreadExecutor创建的线程数量都不会超过 corePoolSize(超过corePoolSize的情况是缓冲队列已满,需要再创建线程来执行到来的任务,前提是当前的线程数量依旧没有超过maximumPoolSize)。

2. 我们可以再来看看三个线程池采用的ThreadPoolExecutor构造方法都是同一个,使用的都是默认的ThreadFactory和handler:

同样的构造方法,同样的ThreadFactory,同样的handler:



我们来看下handler:



也就说三个线程池创建的线程对象都是同组,优先权等级为正常的Thread.NORM_PRIORITY(5)的非守护线程,使用的被拒绝任务处理方式是直接抛出异常的AbortPolicy策略。


参考文献:

1. Java 7之多线程线程池 - 线程池原理(1)

2. Java Executor并发框架(一)整体介绍

3. Java 7之多线程线程池 - 线程池原理(1)

4. Java 线程池原理和队列详解

5. 从使用到原理学习Java线程池

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值