说起Java 7的Executors框架的线程池,同学们能想到有几种线程池,它们分别是什么?

  一共有四个,它们分别是Executors的 newSingleThreadPool(), newCachedThreadPool(), newFixedThreadPool(),newScheduledThread(),四个静态方法,当然在java 8中还有一个newWorkStealingThreadPool()。

  但今天这些这些不是咱们今天要说的重点,今天要说的重点是里边所使用的ThreadPoolExecutor, 这个是咱们要说的重点,咱们打开newSingleThreadExecutor()的源码,这个定一个线程的线程池。

  1. /**
  2. * Creates an Executor that uses a single worker thread operating
  3. * off an unbounded queue. (Note however that if this single
  4. * thread terminates due to a failure during execution prior to
  5. * shutdown, a new one will take its place if needed to execute
  6. * subsequent tasks.) Tasks are guaranteed to execute
  7. * sequentially, and no more than one task will be active at any
  8. * given time. Unlike the otherwise equivalent
  9. * <tt>newFixedThreadPool(1)</tt> the returned executor is
  10. * guaranteed not to be reconfigurable to use additional threads.
  11. *
  12. * @return the newly created single-threaded Executor
  13. */
  14. public static ExecutorService newSingleThreadExecutor() {
  15. return new FinalizableDelegatedExecutorService
  16. (new ThreadPoolExecutor(1, 1,
  17. 0L, TimeUnit.MILLISECONDS,
  18. new LinkedBlockingQueue<Runnable>()));
  19. }

  重点来了,里边有5个参数,分别是核心线程数,也就是启动的时候线程池里需要有的线程数,如果设置allowsCoreThreadTimeOut,那么核心线程数就会在KeepAliveTime之后核心线程空闲的也会停止掉;第二个maximumPoolSize是最大线程数,注意,最大线程数是包括核心线程数的;KeepAliveTime是那些超过了核心线程的其他线程在单位时间内停止掉;TimeUnit是时间单位;BlockingQueue是阻塞队列,用于存放那些超过了核心执行线程之外的任务。现在有一个问题,如果任务数超过了核心线程数,那么多余的任务是进入maximumPoolSize,还是进入组则队列呢?

  1. /**
  2. * Creates a new {@code ThreadPoolExecutor} with the given initial
  3. * parameters and default thread factory and rejected execution handler.
  4. * It may be more convenient to use one of the {@link Executors} factory
  5. * methods instead of this general purpose constructor.
  6. *
  7. * @param corePoolSize the number of threads to keep in the pool, even
  8. * if they are idle, unless {@code allowCoreThreadTimeOut} is set
  9. * @param maximumPoolSize the maximum number of threads to allow in the
  10. * pool
  11. * @param keepAliveTime when the number of threads is greater than
  12. * the core, this is the maximum time that excess idle threads
  13. * will wait for new tasks before terminating.
  14. * @param unit the time unit for the {@code keepAliveTime} argument
  15. * @param workQueue the queue to use for holding tasks before they are
  16. * executed. This queue will hold only the {@code Runnable}
  17. * tasks submitted by the {@code execute} method.
  18. * @throws IllegalArgumentException if one of the following holds:<br>
  19. * {@code corePoolSize < 0}<br>
  20. * {@code keepAliveTime < 0}<br>
  21. * {@code maximumPoolSize <= 0}<br>
  22. * {@code maximumPoolSize < corePoolSize}
  23. * @throws NullPointerException if {@code workQueue} is null
  24. */
  25. public ThreadPoolExecutor(int corePoolSize,
  26. int maximumPoolSize,
  27. long keepAliveTime,
  28. TimeUnit unit,
  29. BlockingQueue<Runnable> workQueue) {
  30. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
  31. Executors.defaultThreadFactory(), defaultHandler);
  32. }

  我写了一个测试的例子,让大家更好的理解它的原理。线程池满了之后会有一个rejection的策略,所以我提前写了简单的一个。

  1. package com.hqs.core;
  2.  
  3. import java.util.concurrent.RejectedExecutionHandler;
  4. import java.util.concurrent.ThreadPoolExecutor;
  5.  
  6. public class RejectedFullHandler implements RejectedExecutionHandler {
  7.  
  8. @Override
  9. public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
  10. System.out.println(r.toString() + " I am rejected!");
  11. }
  12.  
  13. }

  然后写线程池测试类,为了大家的阅读方便,我添加了一些注释。

  1. package com.hqs.core;
  2.  
  3. import java.util.concurrent.ArrayBlockingQueue;
  4. import java.util.concurrent.BlockingQueue;
  5. import java.util.concurrent.CountDownLatch;
  6. import java.util.concurrent.ExecutionException;
  7. import java.util.concurrent.ThreadPoolExecutor;
  8. import java.util.concurrent.TimeUnit;
  9.  
  10. public class ThreadPoolExecutorTest implements Runnable {
  11. private int i;
  12. private CountDownLatch cdl;
  13. public ThreadPoolExecutorTest(int i, CountDownLatch cdl) {
  14. this.i = i;
  15. this.cdl = cdl;
  16. }
  17. public void run() {
  18. try {
  19. TimeUnit.SECONDS.sleep(3);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23. System.out.println(i + " is running");
  24. cdl.countDown();
  25. }
  26. @Override
  27. public String toString() {
  28. return "i:" + i;
  29. }
  30.  
  31. public static void main(String[] args) throws InterruptedException, ExecutionException {
  32. BlockingQueue queue = new ArrayBlockingQueue<>(2); //定义一个阻塞队列,长度为2
  33. CountDownLatch cdl = new CountDownLatch(5); //设置一个减门闩,用于递减动作
  1.      //定义了2个核心线程,3个最大线程,空闲线程持续5秒钟,阻塞队列,拒绝处理执行类
         ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 3, 5, TimeUnit.SECONDS, queue, new RejectedFullHandler());
  1.      
  2. //threadPool.allowCoreThreadTimeOut(true); //设置是否将空闲的核心线程超时后停止
  3. for(int i = 1; i <= 6; i++ ) {
  4. ThreadPoolExecutorTest t1 = new ThreadPoolExecutorTest(i, cdl);
  5. threadPool.execute(t1);
  6. // FutureTask future = (FutureTask)threadPool.submit(t1); //此处注释,因为submit传的参数是Runnable而不是Callable,所以返回结果为null
  7. System.out.println("queue content:" + queue.toString()); //将queue的内容打印出来
  8. }
  9. try {
  10. cdl.await(); //所有线程执行完之后,主线程继续往下进行。
  11. } catch (InterruptedException e1) {
  12. e1.printStackTrace();
  13. }
  14. try {
  15. TimeUnit.SECONDS.sleep(6);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. System.out.println("poosize :" + threadPool.getPoolSize()); //输出PoolSize
  20. threadPool.shutdown(); //注意,所以线程池的最后都必须要显示的关闭
  21. }
  22. }
  23. queue content:[]
  24. queue content:[]
  25. queue content:[i:3]
  26. queue content:[i:3, i:4]
  27. queue content:[i:3, i:4]
  28. i:6 I am rejected!
  29. queue content:[i:3, i:4]
  30. 1 is running
  31. 2 is running
  32. 5 is running
  33. 3 is running
  34. 4 is running
  35. poosize :2

  我简单解释一下这个输出结果,首先线程池初始化的时候启动两个线程,这时1,2进入到池子进行执行,但是1,2程序还没有执行完,紧接着3,4被放到了Queue队列里边,程序发现最大线程池是3个,目前2个核心线程池正在执行,还没有达到最大值,所以启用一个线程,执行5。当6进来的发现池子已经满了,队列也满了,此时只能reject掉了,所以会走reject的异常处理。

  在此,程序执行的顺序为 corePoolSize -> workQueue -> maximumPoolSize-corePoolSize -> RejectExecutionHandler (如果池子满了)

  还有一个比较重要的点,也是同学们想知道的点,那就是execute()和submit()的区别是什么? 

  1. execute方法是void的,没有返回值,而submit()方法返回的是Future对象。当然还有一些抛出的异常会不一致,这里就不详述了。

  2. execute方法是在Executor的service中定义的,而submit方法是在ExecutorService中定义的。

  3. execute方法只支持Runnable参数,而submit方法除了支持Runnable外还支持Callable参数,也就意味着通过submit执行的结果是可以返回的,通过Future.get()方法,也就意味着并发计算的结果是可以返回的(这就是两者为什么区别的根本原因)

  那么很多同学也会问,两个方法都是异步执行的,那什么情况下用execute或submmit方法呢?

  比如多线程并行执行,不需要执行结果返回的时候一般使用execute方法,如果需要多线程并行计算,并且都需要返回结果的时候,需要submmit方法,当然submmit一般情况下是blocking的,如果想在制定的时间取回结果,如果取不到就会抛异常是通过get()方法设置参数,一般同时处理大量文件的时候,将开启多个线程对文件内容分别处理并将结果返回的再统计或汇总的时候比较方便。

  

Core Java 谈谈 ThreadPoolExecutor的更多相关文章

  1. Core Java 谈谈HashMap

    说起Java的HashMap相信大家都不是很陌生,但是对于HashMap内部结构有些同学可能不太了解,咱们下一步就将其展开. HashMap是基于Hash算法的,同理的还有HashSet和HashTa ...

  2. applet示例 WelcomeApplet.java <Core Java>

    import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.Font; import java.awt.Grap ...

  3. Core Java Volume I — 1.2. The Java "White Paper" Buzzwords

    1.2. The Java "White Paper" BuzzwordsThe authors of Java have written an influential White ...

  4. Core Java Volume I — 4.7. Packages

    4.7. PackagesJava allows you to group classes in a collection called a package. Packages are conveni ...

  5. Core Java Interview Question Answer

    This is a new series of sharing core Java interview question and answer on Finance domain and mostly ...

  6. Core Java 学习笔记——1.术语/环境配置/Eclipse汉化字体快捷键/API文档

    今天起开始学习Java,学习用书为Core Java.之前有过C的经验.准备把自己学习这一本书时的各种想法,不易理解的,重要的都记录下来.希望以后回顾起来能温故知新吧.也希望自己能够坚持把自己学习这本 ...

  7. Core Java读书笔记之String

    Java里面的String Conceptually, Java Strings are sequences of Unicode characters. Java里面的String都是Unicode ...

  8. Top 25 Most Frequently Asked Interview Core Java Interview Questions And Answers

    We are sharing 25 java interview questions , these questions are frequently asked by the recruiters. ...

  9. Difference Between Arraylist And Vector : Core Java Interview Collection Question

    Difference between Vector and Arraylist is the most common  Core Java Interview question you will co ...

随机推荐

  1. Javascript--cookie创建与查看

    创建cookie 以下代码将创建一个cookie,该cookie名称为UserName,值为Paul,过期时间为7天后(2015年6月29日) <span style="font-si ...

  2. IntelliJ Idea 2017 注册码 免费激活方法

    1. 到网站 http://idea.lanyus.com/ 获取注册码. 2.弹窗中选择最后一个页面license server,填入下面一种链接即可: http://idea.iteblog.co ...

  3. 前后端分离ueditor富文本编辑器的使用-Java版本

    最近在写一个自己的后台管理系统(主要是写着玩的,用来熟悉后端java的知识,目前只是会简单的写点接口),想在项目中编写一个发布新闻文章的功能,想到了使用百度的ueditor富文本编辑器,网上找了很多j ...

  4. 《软件测试的艺术(原书第2版)》【PDF】下载

    <软件测试的艺术(原书第2版)>[PDF]下载链接: https://u253469.ctfile.com/fs/253469-231196343 内容简介 本书以一次自评价测试开篇,从软 ...

  5. mybatis防止sql注入

         SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedi ...

  6. 写给自己的web总结——css篇(1)

    上一篇写了关于html的知识,算是小试牛刀,这次来尝试写一下css. 初步了解css css的全称为cascading style sheet-- 层叠样式表,通过编入代码来对html里的标签做出各种 ...

  7. springboot 入门六-多环境日志配置

    在应用项目开发阶段,需要对日志进入很详细的输出便于排查问题原因,上线发布之后又只需要输出核心的日志信息的场景.springboot也提供多环境的日志配置.使用springProfile属性来标识使用那 ...

  8. Mybatis篇总结

    本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 JDBC写法 //sql: String sql = "s ...

  9. Run a task only once in (akka) cluster

    在stackOverflow网站上看到这一提问,下文是部分摘抄问题简述: Java cluster, run task only once We have a java process, which ...

  10. 基于Vue的SPA动态修改页面title的方法

    最近基于VUE做个SPA手机端web发现动态修改页面标题通过document.title=xxxx 来修改着实蛋疼,而且在IOS的微信端据说没效果.百度发现要针对IOS的微信做点额外的操作,即:创建一 ...