理解java多线程中ExecutorService使用

所属分类: 软件编程 / java 阅读数: 33
收藏 0 赞 0 分享

java.util.concurrent包里提供了关于多线程操作的类,平常用的比较多的是ExecutorService及其实现类(如ThreadPoolExecutor等),Executor,Executors,Future,Callable等

1. ExecutorService(继承自Executor)接口:提供了一些异步的多线程操作方法,如execute(), submit(), shutdown(), shutdownNow()等

2. Executor接口:执行提交的任务(线程),只有一个方法 execute(Runnable a)

2. Executors类: 提供了一些工厂方法和一些公共方法来操作Executor子类和ThreadFactory等,如newXXX(),xxxThreadFactory()等

3. Futrue接口:代表了线程执行结果,提供了获取线程执行结果和取消线程的方法,如get(),cancle()等

4. Callable接口:JDK1.5提供的有返回值的线程执行新接口

对ExecutorService和Future的理解做简单记录

代码:

public class Main {
  private static int count = 0;

  public static void main(String[] args){
    List<Future> resultList = new LinkedList<>();

    /**
     * Executors.newCachedThreadPool() 创建一个线程缓存池,若60s中线程没有被使用,则会停止线程并从缓存池中移除
     * Executors.newScheduledThreadPool() 创建一个固定容量的线程池,里边的线程按照设定的调度时间执行
     * Executors.newFixedThreadPool()  拥有固定容量的线程缓存池
     * Executors.newSingleThreadExecutor() 容量为一的线程缓存池,只会有一个线程
     */
    ExecutorService executorService = Executors.newCachedThreadPool();
    for(int i=0; i<10; i++){
      Future future = executorService.submit(new Callable<String>() {
        @Override
        public String call() {
          try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(5000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          int count = Main.count;
          System.out.println(Thread.currentThread().getName() + "..start Main count:..." + count);
          Main.count = ++count;
          System.out.println(Thread.currentThread().getName() + "..end Main count:..." + Main.count);
          return Thread.currentThread().getName();
        }
      });
      resultList.add(future);
    }
    executorService.shutdown();
    for(Future future: resultList){
      try {
        System.out.println(future.get() + "..is over...");
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (ExecutionException e) {
        e.printStackTrace();
      }
    }
    System.out.println("main thread end...");
  }
}

输出:

pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-6
pool-1-thread-7
pool-1-thread-8
pool-1-thread-9
pool-1-thread-10
pool-1-thread-1..start Main count:...0
pool-1-thread-2..start Main count:...0
pool-1-thread-3..start Main count:...1
pool-1-thread-2..end Main count:...1
pool-1-thread-1..end Main count:...1
pool-1-thread-3..end Main count:...2
pool-1-thread-1..is over...
pool-1-thread-2..is over...
pool-1-thread-4..start Main count:...2
pool-1-thread-3..is over...
pool-1-thread-4..end Main count:...3
pool-1-thread-4..is over...
pool-1-thread-5..start Main count:...3
pool-1-thread-5..end Main count:...4
pool-1-thread-5..is over...
pool-1-thread-6..start Main count:...4
pool-1-thread-6..end Main count:...5
pool-1-thread-6..is over...
pool-1-thread-7..start Main count:...5
pool-1-thread-7..end Main count:...6
pool-1-thread-7..is over...
pool-1-thread-8..start Main count:...6
pool-1-thread-8..end Main count:...7
pool-1-thread-8..is over...
pool-1-thread-9..start Main count:...7
pool-1-thread-9..end Main count:...8
pool-1-thread-9..is over...
pool-1-thread-10..start Main count:...8
pool-1-thread-10..end Main count:...9
pool-1-thread-10..is over...
main thread end... //主线程在所有线程执行完成后结束

控制台在等待5秒后打印出上边的输出结果,原因是所有的线程启动的时候是一个并发操作,都会去等待5秒,所以整体看来只等了5秒,这是一个并发操作

总结:

1. ExecutorService提供的execute()方法和submit()方法的区别:

  a. execute()方法只接受Runnable类型的实例,所以不能拿到返回值,也不能动态获取线程执行的情况

  b. submit()方法接受Runnable和Callable实例,会返回Future实例,Future实例的get()方法可以获取线程执行返回值,并能抛出线程执行异常。所以如果要获取线程执行返回的结果,并能处理线程执行时可能出现的异常,或者想中途取消线程执行时可以使用submit()方法

2. 通过输出可以看到main方法(主线程)在所有线程执行完成后结束,原因:

  a. 通过submit()方法获取Future实例,并通过Future实例的get()方法获取线程返回结果,而Future实例的get()方法会等待线程执行完毕才会返回,所以main方法会等待所有子线程结束才会结束

  b. 若去掉上边红色标注的for循环,则main方法(主线程)会提前结束,而不会等待所有子线程结束

补充:

1. 多个线程并发执行时,若其中某一个线程出现了异常并且没有被处理,则该线程会自动停止执行,但其他线程还是会正常执行,这就是为什么tomcat请求出现异常时,tomcat还可以继续提供服务的原因。

2. tomcat提供了线程池和等待池,每一个请求过来都会重新启动一个新的线程处理该请求,若线程池中线程用完,再来请求的时候就会放到等待池中等待,当其中有线程释放回线程池中时,就会为等待池中的请求分配线程处理请求。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

Java的面向对象编程基本概念学习笔记整理

这篇文章主要介绍了Java的面向对象编程基本概念学习笔记整理,包括类与方法以及多态等支持面向对象语言中的重要特点,需要的朋友可以参考下
收藏 0 赞 0 分享

Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法

这篇文章主要介绍了Eclipse下编写java程序突然不会自动生成R.java文件和包的解决办法 的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

基于Java实现杨辉三角 LeetCode Pascal's Triangle

这篇文章主要介绍了基于Java实现杨辉三角 LeetCode Pascal's Triangle的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Java中Spring获取bean方法小结

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架,如何在程序中获取Spring配置的bean呢?下面通过本文给大家介绍Java中Spring获取bean方法小结,对spring获取bean方法相关知识感兴趣的朋友一起学习吧
收藏 0 赞 0 分享

如何计算Java对象占用了多少空间?

在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下
收藏 0 赞 0 分享

剖析Java中的事件处理与异常处理机制

这篇文章主要介绍了Java中的事件处理与异常处理机制,讲解Java是如何对事件或者异常作出响应以及定义异常的一些方法,需要的朋友可以参考下
收藏 0 赞 0 分享

详解Java的Struts2框架的结构及其数据转移方式

这篇文章主要介绍了详解Java的Struts2框架的结构及其数据转移方式,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

Java封装好的mail包发送电子邮件的类

本文给大家分享了2个java封装好的mail包发送电子邮件的类,并附上使用方法,小伙伴们可以根据自己的需求自由选择。
收藏 0 赞 0 分享

在Java的Struts中判断是否调用AJAX及用拦截器对其优化

这篇文章主要介绍了在Java的Struts中判断是否调用AJAX及用拦截器对其优化的方法,Struts框架是Java的SSH三大web开发框架之一,需要的朋友可以参考下
收藏 0 赞 0 分享

java多线程Future和Callable类示例分享

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。今天我们就来研究下Future和Callab
收藏 0 赞 0 分享
查看更多