Java多线程 CompletionService和ExecutorCompletionService

2023-05-16

目录

  • 一、说明
  • 二、理解
  • 三、实现
    • 1.使用Future
    • 2.使用ExecutorCompletionService
    • 3.take()方法
    • 4.poll()方法
    • 5.poll(long timeout, TimeUnit unit)方法

一、说明

Future的不足

  • 当通过 .get() 方法获取线程的返回值时,会导致阻塞
  • 也就是和当前这个Future关联的计算任务真正执行完成的时候才返回结果
  • 新任务必须等待已完成任务的结果才能继续进行处理,会浪费很多时间,最好是谁最先执行完成谁最先返回

CompletionService的引入

  • 解决阻塞的问题
  • 以异步的方式一边处理新的线程任务,一边处理已完成任务的结果,将执行任务与处理任务分开进行处理

二、理解

CompletionService

  • java.util.concurrent包下CompletionService<V>接口,但并不继承Executor接口,仅有一个实现类ExecutorCompletionService用于管理线程对象
  • 更加有效地处理Future的返回值,避免阻塞,使用.submit()方法执行任务,使用.take()取得已完成的任务,并按照完成这些任务的时间顺序处理它们的结果
public interface CompletionService<V> {
    Future<V> submit(Callable<V> task);
    Future<V> submit(Runnable task, V result);
    Future<V> take() throws InterruptedException;
    Future<V> poll();
    Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
  • submit()方法用来执行线程任务
  • take()方法从队列中获取完成任务的Future对象,谁最先执行完成谁最先返回,获取到的对象再调用.get()方法获取结果
  • poll()方法获取并删除代表下一个已完成任务的 Future,如果不存在,则返回null,此无阻塞的效果
  • poll(long timeout, TimeUnit unti) timeout表示等待的最长时间,unit表示时间单位,在指定时间内还没获取到结果,则返回null

ExecutorCompletionService

  • java.util.concurrent包下ExecutorCompletionService<V>类实现CompletionService<V>接口,方法与接口相同
  • ExecutorService可以更精确和简便地完成异步任务的执行
  • executor执行任务,completionQueue保存异步任务执行的结果
public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    private final BlockingQueue<Future<V>> completionQueue;
    ……
    Future<V> submit(Callable<V> task) 
    Future<V> submit(Runnable task, V result) 
    Future<V> take() throws InterruptedException
    Future<V> poll() 
    Future<V> poll(long timeout, TimeUnit unit)
    ……
}
  • completionQueue初始化了一个LinkedBlockingQueue类型的先进先出阻塞队列
    public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }
  • submit()方法中QueueingFutureExecutorCompletionService中的内部类
    public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        executor.execute(new QueueingFuture<V>(f, completionQueue));
        return f;
    }
  • QueueingFutureRunnableFuture实例对象赋值给了task,内部的done()方法将task添加到已完成阻塞队列中,调用take()poll()方法获取已完成的Future
    private static class QueueingFuture<V> extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task,
                       BlockingQueue<Future<V>> completionQueue) {
            super(task, null);
            this.task = task;
            this.completionQueue = completionQueue;
        }
        private final Future<V> task;
        private final BlockingQueue<Future<V>> completionQueue;
        protected void done() { completionQueue.add(task); }
    }

三、实现

1.使用Future

创建CompletionServiceDemo类,创建好的线程对象,使用Executors工厂类来创建ExecutorService的实例(即线程池),通过ThreadPoolExecutor.submit()方法提交到线程池去执行,线程执行后,返回值Future可被拿到

public class CompletionServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 2.创建Callable子线程对象任务
        Callable callable_1 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_2 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_3 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        // 3.使用Future提交三个任务到线程池
        Future future_1 = executorService.submit(callable_1);
        Future future_2 = executorService.submit(callable_2);
        Future future_3 = executorService.submit(callable_3);

        // 4.获取返回值
        System.out.println("开始获取结果 " + getStringDate());
        System.out.println(future_1.get() + "" + getStringDate());
        System.out.println(future_2.get() + "" + getStringDate());
        System.out.println(future_3.get() + "" + getStringDate());
        System.out.println("结束 " + getStringDate());
        
        // 5.关闭线程池
        executorService.shutdown();
    }

    // 获取时间函数
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        String date = simpleDateFormat.format(currentTime);
        return date;
    }
}

future_1.get()会等待执行时间阻塞5秒再获取到结果,而在这5秒内future_2future_3的任务已完成,所以会立马得到结果
在这里插入图片描述

2.使用ExecutorCompletionService

创建一个ExecutorCompletionService放入线程池实现CompletionService接口,将创建好的线程对象通过CompletionService提交任务和获取结果

public class CompletionServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        
        // 2.创建一个ExecutorCompletionService放入线程池实现CompletionService接口
        CompletionService completionService = new ExecutorCompletionService(executorService);
        
        // 3.创建Callable子线程对象任务
        Callable callable_1 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_2 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_3 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        // 3.使用CompletionService提交三个任务到线程池
        completionService.submit(callable_1);
        completionService.submit(callable_2);
        completionService.submit(callable_3);

        // 4.获取返回值
        System.out.println("开始获取结果 " + getStringDate());
        System.out.println(completionService.take().get() + "" + getStringDate());
        System.out.println(completionService.take().get() + "" + getStringDate());
        System.out.println(completionService.take().get() + "" + getStringDate());
        System.out.println("结束 " + getStringDate());

        // 5.关闭线程池
        executorService.shutdown();
    }

    // 获取时间函数
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        String date = simpleDateFormat.format(currentTime);
        return date;
    }
}

提交顺序是1-2-3,按照完成这些任务的时间顺序处理它们的结果,返回顺序是3-2-1
在这里插入图片描述

3.take()方法

take()方法从队列中获取完成任务的Future对象,会阻塞,一直等待线程池中返回一个结果,谁最先执行完成谁最先返回,获取到的对象再调用.get()方法获取结果

如果调用take()方法的次数大于任务数,会因为等不到有任务返回结果而阻塞,只有三个任务,第四次take等不到结果而阻塞
在这里插入图片描述

4.poll()方法

poll()方法不会去等结果造成阻塞,没有结果则返回null,接着程序继续往下运行

直接用completionService.poll().get()会引发 NullPointerException
在这里插入图片描述
创建一个循环,连续调用poll()方法,每次隔1秒调用,没有结果则返回null
在这里插入图片描述

public class CompletionServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        // 2.创建一个ExecutorCompletionService放入线程池实现CompletionService接口
        CompletionService completionService = new ExecutorCompletionService(executorService);
        // 3.创建Callable子线程对象任务
        Callable callable_1 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_2 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_3 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        // 3.使用CompletionService提交三个任务到线程池
        completionService.submit(callable_1);
        completionService.submit(callable_2);
        completionService.submit(callable_3);

        // 4.获取返回值
        System.out.println("开始获取结果 " + getStringDate());

        // 5.创建一个循环,连续调用poll()方法,间隔1秒
        for (int i = 0; i < 8; i++) {
            Future future = completionService.poll();
            if (future!=null){
                System.out.println(future.get() + getStringDate());
            }else {
                System.out.println(future+" "+getStringDate());
            }
            Thread.sleep(1000);
        }  
        System.out.println("结束 " + getStringDate());

        // 6.关闭线程池
        executorService.shutdown();
    }

    // 获取时间函数
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        String date = simpleDateFormat.format(currentTime);
        return date;
    }
}

5.poll(long timeout, TimeUnit unit)方法

poll(long timeout, TimeUnit unit)方法设置了等待时间,等待超时还没有结果就返回null

不使用 Thread.sleep(1000),将等待时间设置成0.5秒,由于只有8次循环,也就是4秒执行时间,而callable_1需要执行5秒,获取不到结果则返回null
在这里插入图片描述

public class CompletionServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        // 2.创建一个ExecutorCompletionService放入线程池实现CompletionService接口
        CompletionService completionService = new ExecutorCompletionService(executorService);
        // 3.创建Callable子线程对象任务
        Callable callable_1 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(5000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_2 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(3000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        Callable callable_3 = new Callable() {
            @Override
            public String call() throws Exception {
                Thread.sleep(1000);
                return ("我是Callable子线程 " +Thread.currentThread().getName()+ " 产生的结果 " );
            }
        };

        // 3.使用CompletionService提交三个任务到线程池
        completionService.submit(callable_1);
        completionService.submit(callable_2);
        completionService.submit(callable_3);

        // 4.获取返回值
        System.out.println("开始获取结果 " + getStringDate());

        // 5.创建一个循环,连续调用poll()方法,间隔1秒
        for (int i = 0; i < 8; i++) {
            Future future = completionService.poll(500, TimeUnit.MILLISECONDS);
            if (future!=null){
                System.out.println(future.get() + getStringDate());
            }else {
                System.out.println(future+" "+getStringDate());
            }
        }
        System.out.println("结束 " + getStringDate());

        // 6.关闭线程池
        executorService.shutdown();
    }

    // 获取时间函数
    public static String getStringDate() {
        Date currentTime = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
        String date = simpleDateFormat.format(currentTime);
        return date;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java多线程 CompletionService和ExecutorCompletionService 的相关文章

  • 如何在jupyter notebook中播放视频(不停地展示图片)

    在解决图像处理问题的时候 xff0c 可以利用opencv打开视频 xff0c 并一帧一帧地show出来 xff0c 但是要用到imshow xff0c 需要本地的界面支持 代码如下 span class token comment cod
  • CentOS7配置VNC远程桌面

    桌面还是有很多方便的地方 xff0c 在用U盘给电脑安装了centos7 xff08 带gnome xff09 后 xff0c 接着就需要弄远程桌面 xff08 1 xff09 安装vncserver yum y install tiger
  • 10:子查询-MySQL

    目录 10 1 子查询基本语法10 2 in 和 not in10 3 exists 和 not exists10 4 基础结束语 10 1 子查询基本语法 将一个查询的结果作为另一个查询的数据来源或判断条件 一般情况下子查询结果返回超过1
  • 11:高级部分-MySQL

    目录 xff08 一 xff09 view 视图1 开场2 view视图创建 使用以及作用3 显示视图4 更新和删除视图5 视图算法 xff1a temptable merge xff08 二 xff09 transaction 事务1 事
  • 12:企业规范约束-MySQL

    目录 12 1 库表字段约束规范12 2 索引规范12 3 SQL开发约束规范12 4 其他规范 12 1 库表字段约束规范 字段名 xff1a is vip unsigned tinyint 长度为1mysql命名是不区分大小写的 xff
  • Java与生活

    目录 一带而过 0 0 前言 1 1 Java是怎么执行的 xff1f 说好的exe呢 xff1f 1 2 package 1 3 第一个程序的讲解 1 4 注释和文档 2 0 一带而过 2 1 字符串演示 2 2 字符串结束符的那些事儿
  • 一带而过-Java与生活

    目录 认识Java0 0 前言1 1 Java是怎么执行的 xff1f 说好的exe呢 xff1f 1 2 package1 3 第一个程序的讲解1 4 注释和文档2 0 一带而过2 1 字符串演示2 2 字符串结束符的那些事儿2 3 自动
  • 0:Linux的初步认识-步入Linux的现代方法

    目录 0 0 系统的认识0 1 Linux操作系统认识 xff0c 以及开源的提出 xff1a Linux的千奇百怪的版本0 2 开源的含义0 3 Linux的用途 xff0c 各类发行版本 0 0 系统的认识 什么是系统 xff1f 鼠标
  • 1:VMware虚拟机的使用-步入Linux的现代方法

    目录 1 1 Vmware虚拟机1 2 VMware 161 3 关于从15更新到16的说法 1 1 Vmware虚拟机 安装系统的方式 实体机双系统虚拟机 详见 xff1a VMware Workstation 虚拟机权威指南 零基础虚拟
  • 2:发行版本安装演示——Ubuntu、CentOS、Kali?-步入Linux的现代方法

    目录 2 1 先尝试Ubuntu2 2 VMware Tools2 3 Ubuntu 20 04设置2 4 Linux其他发行版本的安装指导2 5 下载指导 2 1 先尝试Ubuntu 详见 xff1a Linux Ubuntu 零基础体验
  • 3:步入Linux的世界-步入Linux的现代方法

    目录 3 1 Linux究竟需要我们学习什么 xff1f Linux四大组成部分3 2 Linux是命令还是图形界面 xff1f GUI 是什么 xff1f 那GNU是什么东西 xff1f GNU Linux 和Linux有什么区别 xff
  • Linux中 sudo su 和 su 区别

    su 直接切换root用户 xff0c 需输入root密码ubuntu xff0c 默认没有设置root密码 xff0c 使用sudo passwd root设置root密码 sudo su 当前用户暂时申请root权限 xff0c 需输入
  • Python 使用 Qt5 实现水平导航栏

    在 Qt5 中可以使用 QWidget 包含两个水平布局 xff0c 通过点击水平布局里的按钮 xff0c 实现下标滑动与页面的切换 可以按照以下步骤来实现上面图片中的功能 xff1a 导入必要的 Qt 包 xff1a span class
  • OOP上半部分-Java与生活

    目录 1 1 1 问题产生和引导1 1 2 烦人1 1 3 变换思维1 1 4 规划明确目标站在更高层次思考问题1 1 5 上代码 xff0c 设计体验面向对象编程 xff0c 实例和对象1 1 6 去你md成员变量行为类和this1 1
  • Centos7 搭建Jupyter NoteBook教程

    目录 1 Anaconda31 1 下载1 2 安装 2 环境配置2 1 添加PATH到 root bashrc文件中2 2 激活配置的环境变量 3 搭建虚拟环境3 1 创建虚拟环境3 2 开启环境3 3 查看已有的虚拟环境 4 jupyt
  • OOP下半部分-Java与生活

    目录 面向对象三大特性 xff1a 封装 继承 多态2 1 1 需求重定义2 1 2 继承2 2 2 饿狼传说之多层继承2 2 3 方法的重写2 2 4 super啃老2 2 5 啃老啃到彻底2 2 6 final2 2 7 提出新的问题2
  • Centos7 搭建单机Spark分布式集群

    目录 1 JDK Hadoop Spark安装与配置1 1 解压包1 2 配置环境变量 2 Scala安装与配置2 1 Scala安装2 2 配置环境变量 3 配置集群3 1 配置sprak3 2 启动spark 4 问题 xff1a 虚拟
  • 面向对象大胆向前 Java API 实战

    目录 0 xff1a Base API 引言API的定义和用处ScannerNumberMathRandomThreadLocalRandomDateDateFormat和SimpleDateFormatCalendarSystem 详见
  • Yeats_Liao的书单

    计算机软件类 大话计算机 冬瓜哥 架构师的自我修炼 李智慧 图解算法 xff1a 使用C语言 吴灿铭 胡昭民 编程原则 马克思 卡纳特 亚历山大 啊哈 xff01 算法 啊哈磊 Java Web框架开发技术 Spring 43 Spring
  • 0:Base API-Java API 实战

    目录 0 1 引言0 2 API的定义和用处0 3 Scanner xff08 普通类 xff09 0 4 Number xff08 包装类 xff09 0 5 Math xff08 工具类 xff09 0 6 Random xff08 父

随机推荐

  • 黑客与画家 [美] Paul Graham 读书摘录

    充分理解程序员带来的美和智慧 xff0c 这是本书做到的 P15 为什么书呆子不受欢迎 xff1f 平庸带来的严重后果 xff0c 直接导致学生的叛逆心理 我误解最深的一个词是 老成 tact 成年人使用这个词 xff0c 含义似乎就是 闭
  • 教育的真谛 [英] 尼古拉斯·泰特 读书摘录

    自柏拉图以来 xff0c 教育的目的与性质始终是西方哲学传统关注和探讨的问题 纵览2500年来的思想成果 xff0c 作者尼古拉斯 泰特博士在 教育的真谛 xff1a 伟大思想家的观点及其现实意义 中指出 xff0c 人类的教育活动至少应包
  • 1:Unit test and main function-Java API 实战

    目录 1 抛出企业问题 xff0c 脱离main测试 xff0c 模块化编程2 Junit单元测试的含义和用途3 怎么获取各种Jar包 xff1f Maven Repository 获取各类各个版本的jar xff0c 这就是仓库 脱离老师
  • CentOS 安装 Samba服务器(多用户组、多用户有不同的访问权限)

    增加smb用户 root 64 localhost sir01 smbpasswd a linuxsir 查看 smb 现有用户 pdbedit L 验证用户登录文件夹 smbclient 192 168 101 93 forlder U
  • 2:StringBuilder-Java API 实战

    目录 1 String存在的问题2 Stringbuilder以及链式调用的含义 1 String存在的问题 认识String 字符串广泛应用在编程中 xff0c 在 Java 中字符串属于对象 xff0c Java 提供了 String
  • 3:Throwable-Java API 实战

    目录 1 异常的介绍2 异常举例以及解决常见错误bug方案3 RuntimeException4 trycatch作用 xff0c 闲扯淡诱骗毕业设计5 NullPointerException空指针异常6 throws7 throws和t
  • 4:File-Java API 实战

    目录 1 引言2 绝对路径和相对路径 xff1f 先学送快递吧 xff01 3 绝对路径4 相对路径5 File类6 Linux上的绝对路径有所不同 1 引言 文件要区别绝对路径和相对路径 xff0c 在Win系统中的文件路径和Linux
  • 5:IO Stream-Java API 实战

    目录 1 相对论和IO流之说2 汉语文学理解IO流3 图解IO流4 俩亲爹 xff1a InputStream和OutPutStream5 FileInputStream字节流读取文件6 FileOutPutStream字节流写入文件7 b
  • 6:CharSet-Java API 实战

    目录 1 阶段2 字符集编码吹X3 转换字符编码 1 阶段 Java NIO File Java NIO中的Files类 xff08 java nio file Files xff09 提供了多种操作文件系统中文件的方法 Java File
  • 7:Multithreading-Java API 实战

    目录 1 问题的提出2 核心数 进程 线程3 进程和线程的区别以及对应应用4 多线程程序含义 多线程的作用5 多线程的执行过程6 Runnable7 简化操作以及线程名8 抢购鞋 多线程案例9 后台 守护进程的提出10 匿名内部类创建多线程
  • 8:Java Conllections FrameWork-Java API 实战

    目录 1 原生数组带来的问题 xff0c 抛出问题2 Conllections家族3 黑帮的帮规4 ArrayList第一讲5 ArrayList第二讲6 ArrayList第三讲7 Linked链表8 LinkedList一带而过9 提醒
  • 9:JDBC-Java API 实战

    目录 1 说明2 JDBC的由来以及定义3 JDBC体验 xff0c statement executeQuery 查询4 整理和释放5 封装JDBCUtils6 增删改 executeUpdate 7 字符编码问题8 PreparedSt
  • 10:Java人脸识别认证-Java API 实战

    目录 1 提出问题 xff0c 引入SDK的概念2 选择平台3 SDK下载和文档说明4 人脸检测5 人脸对比6 建议和结束语 1 提出问题 xff0c 引入SDK的概念 什么是SDK xff1f 我们并不具备开发人脸识别的能力 xff0c
  • 个人学习笔记附Markdown格式下载

    B S方向 文章链接 xff1a https blog csdn net qq 46207024 article details 114378676 spm 61 1001 2014 3001 5502 下载链接 xff1a https d
  • Ubuntu/Linux 访问 Windows 共享文件夹

    文章目录 Ubuntu Linux 访问 Windows 共享文件夹SMB 协议安装 samba 客户端访问共享文件夹参考资料 Ubuntu Linux 访问 Windows 共享文件夹 SMB 协议 Linux 操作系统与 Windows
  • Java 序列化与反序列化

    目录 一 说明二 理解三 实现1 实现接口2 序列化方法3 反序列化方法 一 说明 序列化与反序列化是什么 序列化 xff1a 将Java对象表示为一个字节序列 xff0c 存储对象数据到文件中 xff0c 可用于网络传输 反序列化 xff
  • JavaMail 使用POP3/SMTP服务发送QQ邮件

    目录 一 说明二 理解三 实现1 导入jar包2 用户认证3 发送邮件创建步骤简单的Email带HTML的E mail带图片的Email包含附件的邮件 一 说明 邮件服务器 为用户提供接收邮件的功能 xff0c 有发邮件的SMTP服务器 x
  • Java多线程 Callable和Future

    目录 一 说明二 理解三 实现1 实现接口2 执行线程 一 说明 Java 提供了三种创建线程的方法 实现 Runnable接口继承 Thread类本身通过 Callable和 Future 创建线程 Callable和Future的引入
  • Java多线程 Future和FutureTask的区别

    目录 一 说明二 理解三 实现1 实现接口2 使用Future3 使用FutureTask 一 说明 Future和FutureTask的关系 Future 是一个接口 xff0c 无法直接创建对象 xff0c 需配合线程池使用 submi
  • Java多线程 CompletionService和ExecutorCompletionService

    目录 一 说明二 理解三 实现1 使用Future2 使用ExecutorCompletionService3 take 方法4 poll 方法5 poll long timeout TimeUnit unit 方法 一 说明 Future