一、前述

new Thread的弊端

执行一个异步任务你还只是如下new Thread吗?

new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub
    }
}).start();

那你就out太多了,new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

二、ExecutorService介绍

ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中,在这个接口中定义了和后台任务执行相关的方法: 

Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类(详细了解这两个实现类,点击这里):

1. ThreadPoolExecutor
2. ScheduledThreadPoolExecutor
  • 1
  • 2
  • 3

除此之外,ExecutorService还继承了Executor接口(注意区分Executor接口和Executors工厂类),这个接口只有一个execute()方法,最后我们看一下整个继承树: 

三、ExecutorService线程池创建

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
(1). newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {final int index = i;try {Thread.sleep(index * 1000);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(index);}});
}

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
 
(2). newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {System.out.println(index);Thread.sleep(2000);} catch (InterruptedException e) {// TODO Auto-generated catch block
                e.printStackTrace();}}});
}

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。
 
(3) newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("delay 3 seconds");}
}, 3, TimeUnit.SECONDS);

表示延迟3秒执行。
 
定期执行示例代码如下:

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

表示延迟1秒后每3秒执行一次。
ScheduledExecutorService比Timer更安全,功能更强大,后面会有一篇单独进行对比。
 
(4)、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {@Overridepublic void run() {try {System.out.println(index);Thread.sleep(2000);} catch (InterruptedException e) {// TODO Auto-generated catch block
                e.printStackTrace();}}});
}

四、分析


1)、 ExecutorService 的submit() 与execute()区别
1、接收的参数不一样 submit()可以接受runnable无返回值和callable有返回值
execute()接受runnable 无返回值2、submit有返回值,而execute没有Method submit extends base method Executor.execute by creating and returning a Future that can be used to cancel execution and/or wait for completion.用到返回值的例子,比如说我有很多个做validation的task,我希望所有的task执行完,然后每个task告诉我它的执行结果,是成功还是失败,如果是失败,原因是什么。3、submit方便Exception处理There is a difference when looking at exception handling. If your tasks throws an exception and if it was submitted with execute this exception will go to the uncaught exception handler (when you don’t have provided one explicitly, the default one will just print the stack trace to System.err). If you submitted the task with submit any thrown exception, checked or not, is then part of the task’s return status. For a task that was submitted with submit and that terminates with an exception, the Future.get will rethrow this exception, wrapped in an ExecutionException.意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;  public class ExecutorServiceTest {  public static void main(String[] args) {  ExecutorService executorService = Executors.newCachedThreadPool();  List<Future<String>> resultList = new ArrayList<Future<String>>();  // 创建10个任务并执行  for (int i = 0; i < 10; i++) {  // 使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中  Future<String> future = executorService.submit(new TaskWithResult(i));  // 将任务执行结果存储到List中  resultList.add(future);  }  executorService.shutdown();  // 遍历任务的结果  for (Future<String> fs : resultList) {  try {  System.out.println(fs.get()); // 打印各个线程(任务)执行的结果  } catch (InterruptedException e) {  e.printStackTrace();  } catch (ExecutionException e) {  executorService.shutdownNow();  e.printStackTrace();  return;  }  }  }
}  class TaskWithResult implements Callable<String> {  private int id;  public TaskWithResult(int id) {  this.id = id;  }  /** * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。 *  * @return * @throws Exception */  public String call() throws Exception {  System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());  if (new Random().nextBoolean())  throw new TaskException("Meet error in task." + Thread.currentThread().getName());  // 一个模拟耗时的操作  for (int i = 999999999; i > 0; i--)  ;  return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();  }
}  class TaskException extends Exception {  public TaskException(String message) {  super(message);  }
}
执行的结果类似于:
call()方法被自动调用,干活!!!             pool-1-thread-1
call()方法被自动调用,干活!!!             pool-1-thread-2
call()方法被自动调用,干活!!!             pool-1-thread-3
call()方法被自动调用,干活!!!             pool-1-thread-5
call()方法被自动调用,干活!!!             pool-1-thread-7
call()方法被自动调用,干活!!!             pool-1-thread-4
call()方法被自动调用,干活!!!             pool-1-thread-6
call()方法被自动调用,干活!!!             pool-1-thread-7
call()方法被自动调用,干活!!!             pool-1-thread-5
call()方法被自动调用,干活!!!             pool-1-thread-8
call()方法被自动调用,任务的结果是:0    pool-1-thread-1
call()方法被自动调用,任务的结果是:1    pool-1-thread-2
java.util.concurrent.ExecutionException: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222)  at java.util.concurrent.FutureTask.get(FutureTask.java:83)  at com.cicc.pts.ExecutorServiceTest.main(ExecutorServiceTest.java:29)
Caused by: com.cicc.pts.TaskException: Meet error in task.pool-1-thread-3  at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:57)  at com.cicc.pts.TaskWithResult.call(ExecutorServiceTest.java:1)  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)  at java.util.concurrent.FutureTask.run(FutureTask.java:138)  at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)  at java.lang.Thread.run(Thread.java:619) 
可以看见一旦某个task出错,其它的task就停止执行。2)、shotdown() showdownNow()区别可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。 shutdown() 方法在终止前允许执行以前提交的任务, shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。在终止时执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。关闭未使用的 ExecutorService 以允许回收其资源。 一般分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务
// 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。threadPool.shutdown();
3)、Runnable()与Callable()区别如果是一个多线程协作程序,比如菲波拉切数列,1,1,2,3,5,8…使用多线程来计算。 但后者需要前者的结果,就需要用callable接口了。 callable用法和runnable一样,只不过调用的是call方法,该方法有一个泛型返回值类型,你可以任意指定。runnable接口实现的没有返回值的并发编程。  callable实现的存在返回值的并发编程。(call的返回值String受泛型的影响) 使用Future获取返回值。 

结果依次输出,相当于顺序执行各个任务。
现行大多数GUI程序都是单线程的。Android中单线程可用于数据库操作,文件操作,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操作。

ExecutorService--线程池相关推荐

  1. ExecutorService线程池

    ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{ } 2.建立ExecutorService线程池 Execut ...

  2. Java ExecutorService 线程池

    //==采用Java 5的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送请求. //==服务器端: import java.io.BufferedRea ...

  3. Executors源码解读——创建ExecutorService线程池

    Executors源码解读--创建ExecutorService线程池 〇.[源码版本] jdk 1.8 一.线程池概述 二.线程池创建 三.Executors源码解读 newFixedThreadP ...

  4. SpringBoot - 使用ExecutorService线程池执行异步任务教程(以Runnable任务为例)

    有时我们的系统需要进行一些比较耗时的操作,比如用户注册后要调用邮件服务器给用户发送个邮件,又比如上传一个大数据量的 excel 并导入到数据库.如果后端的这些工作比较耗时,那么前台的页面便会一直处于等 ...

  5. 浅析Java线程池 ExecutorService

    ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中. 创建一个什么样的ExecutorService的实例(即线程池)需要g根据具体应用场 ...

  6. 自定义线程池内置线程池的使用 ThreadPoolExecutor和Executorservice 示例与注意事项

    文章目录 线程池介绍 自己设计一个线程池 1.设计ThreadPool类: 2.设计工作队列 3.实现自己设计的线程池 用java的ThreadPoolExecutor自定义线程池 自定义线程池-参数 ...

  7. Android 线程池概念及使用

    一:使用线程池的原因 在android开发中经常会使用多线程异步来处理相关任务,而如果用传统的newThread来创建一个子线程进行处理,会造成一些严重的问题: 在任务众多的情况下,系统要为每一个任务 ...

  8. Java并发编程之线程池及示例

    1.Executor 线程池顶级接口.定义方法,void execute(Runnable).方法是用于处理任务的一个服务方法.调用者提供Runnable 接口的实现,线程池通过线程执行这个 Runn ...

  9. Java的Executor框架和线程池实现原理

    一,Java的Executor框架 1,Executor接口 public interface Executor {void execute(Runnable command);} Executor接 ...

  10. boost创建线程池_Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池

    目录 背景 线程池的来由 什么是线程池 背景总结 用法 通过Executors创建线程池 Executors及其服务的类 Executors常用的几个方法 一个线程池的例子 任务 池子 测试 说明 总 ...

最新文章

  1. exgcd ---- 2020牛客多校第三场:[Fraction Construction Problem:exgcd+思维题]
  2. 培养积极状态的八个方法
  3. 为什么`[`比`子集更好?
  4. SQLServer表内自关联级联删除
  5. 为什么不要使用长事务
  6. android 自定义对话框 demo,自定义dialog对话框获取EditText数据demo
  7. linux 如何赋值目录,Linux文件系统之目录的建立
  8. zc706开发板的linux移植,Zynq—Linux移植学习笔记(十)
  9. 列式存储ClickHouse(二)接口
  10. 在阿里云容器服务中使用定时任务
  11. 开启Github之旅
  12. java怎么销毁session_【Java】问题:Session的销毁方式到底有哪些?
  13. 主流PC常用总线总结
  14. python编写密码登录程序_python初学之用户登录的实现过程(实例讲解)
  15. poj - 2586 - Y2K Accounting Bug
  16. Web前端开源框架 资源库,全,丰富,牛!
  17. 如何将pdf转为word使用?
  18. themeleaf基本语法
  19. Java怎么顺序 或 逆序排序数组
  20. 夜神模拟器(Android7)+BurpSuit配置抓取HTTPS包

热门文章

  1. 计算机的指令合成为,第二章计算机操作基础知识doc
  2. 卡尔曼滤波的基本原理
  3. 第九章 Servlet工作原理解析
  4. UIAlertView, UIAlertViewController
  5. 从偶然的机会发现一个mysql特性到wooyun waf绕过题
  6. Android中用文件初始化sqlite 数据库(二)
  7. NHibernate Profiler使用方法
  8. C# 图片处理之:彩色图片转为黑白图 .
  9. 图片圆角边框自适应宽高(深夜原创)
  10. Java程序员,上班那点事儿