三、线程池

前言:

  在前面的例子中,我们都是手动去实现Runbale、Callable或者继承Thread类来创建一个线程的,但是在实际的开发中,我们通常不会这样做,而是使用线程池,将创建线程的动作交给线程池我们直接拿来用就可以了,线程池相似于数据库连接池,线程池里从放了一定数量已经创建好的线程,我们直接拿来用就可以了,至于线程的销毁什么的都不用我们操心,线程池会负责。

一、为什么使用线程池:

    在我们的实际开发中,线程的创建和销毁都是一件相当耗费资源的工作,并且多线程之间之间的切换执行也是非常消耗资源,因此引入了线程池,使用已经存在的线程,消除了线程创建时的耗时,且可以通过设置线程的数量,防止资源的浪费。

二、线程池类的关系图:

  我们来看一张关于线程池类之间的关系图,其中线程池的顶级接口Executor,在Executor接口中只是提供了一个执行任务的execute()方法,而下面的接口和类都是去继承他和实现它的,同时做了一写扩展。

  

  1、ExecutorService接口:

    从上面看ExecutorService接口直接继承了Executor,下面是ExecutorService接口中的一些方法:我们可以看到ExecutorService在Executor接口原有的功能上有扩展了很多功能,极大的增强了Executor的功能,不仅支持有返回值的任务执行,而且还有很多十分有用的方法来为你提供服务。

    

  2、ScheduledExecutorService接口

    ScheduledExecutorService接口是继承了ExecutorService接口,ScheduledExecutorService接口中的方法相对来说很少,但是却加了特有的调度(schedule)具体方法可以看一下下图:

    

  3、AbstractExecutorService类:

    AbstractExecutorService实现了ExecutorService接口,只是实现了ExecutorService的方法吗,这个没什么好说的,这里也就不截图一笔带过了。

  4、ThreadPoolExecutor类:

    ThreadPoolExecutor类继承了AbstractExecutorService类,在这里ThreadPoolExecutor勒种的方法相当的繁多和复杂,但本质上来说还是对AbstractExecutorService的扩展,并且实现了一些自己特有的方法,例如截图中实现了Executor接口中的excute()方法,重写了AbstractExecutorService中的shutdown方法:

     

   5、ScheduledThreadPoolExecutor类:

    ScheduledThreadPoolExecutor在实现上继承了ThreadPoolExecutor,所以我们依然可以将ScheduledThreadPoolExecutor当成ThreadPoolExecutor来使用,但是从源码中我们可以看出,ScheduledThreadPoolExecutor的功能要强大得多,因为ScheduledThreadPoolExecutor可以根据设定的参数来周期性调度运行,下面的图片展示了四个和周期性相关的方法,当然这只是其中一些方法,ScheduledThreadPoolExecutor还有很多的方法,在这里只是作为了解一下不做过多赘述。

    

二、ThreadPoolExecutor:

  在java中创建线程池,实际上都是调用了ThreadPoolExecutor类中的构造方法,我们来看一下该构造方法都有哪些参数:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
View Code

  corePoolSize:线程池中长期维护的线程数量。

  maximumPoolSize:线程池中能拥有的最大线程数量。

  workQueue:用于缓存任务的阻塞队列,对于不同的场景采用不同的策略,常用的策略有两种:

    SynchronousQueue<Runnable>:此队列不会缓存任务,如果向线程池中提交任务,线程池并没有空闲的线程来执行任务,则入列操作会阻塞,当有线程获取任务出列操作会唤醒      入列操作的线程,从这个特性来看SynchronousQueue是一个无界的队列,因此当使用SynchronousQueue时作为线程池的阻塞队列时,参数maximumPoolSize不会起作用。

    LinkedBlockingQueue<Runnable>:此队列的实现时一个链表结构,可以使有界的也可以是无界的,Executor接口中时无界的

  keepAliveTime:表示空闲线程的存活时间。

  unit:表示keepAliveTime的单位。

  handler:表示当workQueue已满,且线程池中的线程数量打到maximumPoolSize时,线程池拒绝添加新任务采用的策略一般有一下四种取值:

    ThreadPoolExecutor.AborPolicy():抛出RejectedExecutionException异常。

    ThreadPoolExecutor.CallerRunsPolicy():由向线程池提交任务的线程

    ThreadPoolExecutor.DiscardOldestPolicy():抛弃最旧的任务(最先提交而没有执行的任务)

    ThreadPoolExecutor.DiscardPolicy():抛弃当前任务、

  注:在jdk1.8中ThreadPoolExecutor的构造函数中并没有要求传入handler参数,而是在调用构造参数时,默认了ThreadPoolExecutor.AborPolicy()策略

三、四大线程池:

  ThreadPoolExecutor的构造函数有很多参数,用起来很不方便为了方便的创建线程池,javaEE中为我们提供了Executors工具类,Executors提供了四种创建线程池的方法,分别如下

  1、newCachedThreadPool:

    该方法可以创建一个可缓存的线程容器,如果线程池的线长度超过处理需求,可灵活的回收空闲线程,若无可回收,则创建新的线程:

    该线程池的特点:

      (1):工作线程的创建数量几乎没有限制(其实也是有受限制的,数目为Interger.MAX_VALUE)

      (2):空闲的工作线程会自动销毁,有新任务时在创建。

      (3):使用CachedThreadPool时,一定要控制无人数量,否则容易造成大量创建线程,造成系统瘫痪。

  2、newFixedThreadPool:

    该方法创建一个指定数目的线程池,没提交一个任务时就会创建一个工作线程,当线程数达到最大数量时,则将提交的任务存放到池队列中。

    优点:提高程序效率和节省创建线程的的开销。

    缺点:在线程池空闲时,即线程池中没有任务运行时,他不会释放工作线程,还会占用一定的系统资源。

  3、newSingleThreadExecutor:

    该方法创建一个单列的Executor,即只创建一个唯一的工作线程来执行任务,保证所有的任务按照(FIFO,LIFO,优先级)执行,如果这个线程出异常,会有另一个线程来取代它,保证顺序执行。

    工作线程最大的特点是保证任务的执行顺序,并且在任意时间点不会有多余的活动线程。

  4、newScheduleThreadPool:

    该方法创建一个定长的线程池,而且支持定时以及周期性执行任务,

转载于:https://www.cnblogs.com/zouxiangzhongyan/p/11475413.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值