创建线程的第三种方式——实现Callable接口

package com.lqy.Multithreading;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/*
创建线程的第三种方法:实现Callable接口
实现Callable接口是创建线程的三种方法中唯一一个带有返回值的方法
步骤:
1、实现Callable接口
2、重写里面的Call方法(注意是Call不是Run
3、创建Callable实现类的对象
4、将实现类对象作为参数传递给FutureTask构造函数
5、将FutureTask对象作为参数传递给Thread构造函数(因为FutureTask实现了Runnable接口,所以可以这么传)
6、调用Thread类的start方法
这种创建线程的方法一般用于需要很长时间的操作但是我们只需要结果的情况
比如我们需要进行一个算法运算,这个运算的过程非常繁琐,需要很长的时间才能完成,但是我们最终只需要它的运行结果而已
那么我们就可以先在要获取结果前多一段时间就运行该线程,在最终需要结果的地方去获取结果
但是注意,如果执行了获取结果的方法但是那个线程还没有得到结果的话,线程会阻塞直到执行结果出来
 */
public class CallableImpl implements Callable {
    /*
   
在重写Call方法的时候注意
    前面的返回类型你可以自己修改,但是在创建FutureTask类的时候里面的泛型要与之相同
     */
   
@Override
    public Object call() throws Exception {
        System.out.println("CallableImpl");
        return "我是Call方法的返回值";
    }

public static void main(String[] args) {
        CallableImpl callable=new CallableImpl();
        FutureTask<Object> futureTask=new FutureTask<>(callable);
        Thread thread=new Thread(futureTask);
        /*
       
需要注意一件事:
        FutureTask类中的get方法获取返回值只能执行一次
        而且,如果使用了这个方法但是线程还没有运行到可以返回的那行代码,那么就会一直阻塞
        比如如果我在这里执行了如下代码:
        Object result=futureTask.get();
       
那么就永远阻塞了
        当然,我更想说的是,如果你使用的是这种方法创建线程并且需要返回值的话,里面就别写死循环
        否则就是死锁在召唤
         */
       
thread.start();
        try {
            Object result=futureTask.get();
            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

我们来看看Callable类

很显然,它和Runnable类一样,是个功能接口,里面也只有一个方法。

我们接下来看看FutureTask类。

通过一路的追踪源码我们可以知道:

FuterueTask extends RunnableFuture

RunnableFuture extends Runnable,Future

所以我们先来看看Future类

通过查看源码我们可以发现,Future是一个接口,其中定义的接口方法有:

注意,这里只是定义了这些方法,并没有实现,实现这些方法是在FutureTask类中。

接下来我们来看RunnableFuture类

可以发现,它继承了Runnable类和Future类,但是并没有实现他们(自己定义为接口,那么就可以不用实现接口的方法)。

最后来到FutureTask中,根据Java继承的特性可知,Future间接的继承了Runnable类和Future类。

也就是说FutureTask类要实现Run方法和Future类中定义的所有方法。

我们先来看看构造方法:

可以发现,它有两个构造方法:

我们再来看看这个类中的一些方法:

获取线程的允许情况我们可以直接通过状态码知道:

有一个颇有意思的方法:

我们来实验一下:

package com.lqy.Multithreading;import java.util.concurrent.Callable;import java.util.concurrent.FutureTask;public class FutureTaskTest implements Callable {private int i=0;//发现了个有意思的事,它的返回值就算不写也不会报错
@Overridepublic Object call() throws Exception {while (true){System.out.println(i++);}}public static void main(String[] args) {FutureTaskTest futureTaskTest=new FutureTaskTest();FutureTask futureTask=new FutureTask(futureTaskTest);Thread thread=new Thread(futureTask);thread.start();//我们来看看cancel能不能停止该线程
futureTask.cancel(true);}}

运行结果:

可以发现,可以阻止线程运行。

接下来我们来看看我们最经常用的,获取运行结果的Get方法

runAndReset方法会在线程池中使用到,等讲到线程池的时候在详细聊一聊吧。

总结的说:

三种方式:

第一种:Thread,实现了Runnable接口

第二种:Runnable,重写了本类的run方法,使用Thread类的start方法运行

第三种:Callable,重写了本类的call方法,但是作为接收参数的FutureTask间接继承了Runnable接口,其中的run方法实现是将重写的call方法放在run方法中,并加上相应的操作,最后调用Thread类的start方法。

创建线程的第三种方式:实现Callable接口(含部分源码解析)相关推荐

  1. 创建线程的第三种方式---JDK8新特性

    创建线程的第三种方式-JDK8新特性 实现Callable接口,创建线程 Runnable和Callable接口的不同 1.Callable可以返回一个类型V,而Runnable不可以,也就是说Cal ...

  2. Redis源码-String:Redis String命令、Redis String存储原理、Redis String三种编码类型、Redis字符串SDS源码解析、Redis String应用场景

    Redis源码-String:Redis String命令.Redis String存储原理.Redis String三种编码类型.Redis字符串SDS源码解析.Redis String应用场景 R ...

  3. springboot:java实现邮件及附件发送、HTML正文的三种方式(一)【附带源码】

    0. 引言 邮件发送是我们日常开发中比较常见的功能,常用于预警信息提醒.统计数据定期发送等需求.一般该方法会由前人封装好,实际开发时只需要调用即可,但具体怎么实现的,如何从零实现邮件发送,这是我们要掌 ...

  4. JUC-多线程(5.获得线程的第三种方式)学习笔记

    文章目录 获得线程的第三种方式 : Callable接口 1. 前言 1. 获得多线程的方法几种? 2. 以下两种获得线程的方式的异同 2. 使用 1. 重写 call 方法 2.创建线程 3.获取返 ...

  5. java创建线程池几种方式_java知识总结-创建线程池的6种方式

    一.创建线程池的6种方式: Executors.newCachedThreadPool(); 创建一个可缓存线程池,应用中存在的线程数可以无限大 Executors.newFixedThreadPoo ...

  6. java class 生成对象_Java反射机制(创建Class对象的三种方式)

    1:SUN提供的反射机制的类: java.lang.Class java.lang.reflect.Constructor java.lang.reflect.Field java.lang.refl ...

  7. JDBC 创建连接对象的三种方式 、 properties文件的建立、编辑和信息获取

    创建连接对象的三种方式 //第一种方式Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/m ...

  8. 创建线程池的七种方式

    在 Java 语言中,并发编程往往都是通过床架线程池来实现的,而线程池的创建方式也有很多种,每种线程池的创建方式都对应了不同的使用场景.总结来说线程池的创建可以分为两大类: 通过 Executors ...

  9. 创建CSS样式的三种方式

    CSS基础 - 创建CSS样式的三种方式 我们使用CSS的时候,常规有三种创建CSS样式的的方式: 内联样式,内部样式表,外部样式表 1.内联样式 (不常用) style在标签中的情况就是内联样式.不 ...

最新文章

  1. linux下通过yum安装svn及配置
  2. 第五次作业——软件设计
  3. 《系统集成项目管理工程师》必背100个知识点-19项目管理计划的内容
  4. android 在同一activity切换layout,在 Android Studio 添加一个activity并实现跳转
  5. CentOS 7 定时计划任务设置
  6. mysql 1215_mysql执行带外键的sql文件时出现mysql ERROR 1215 (HY000): Cannot add foreign key constraint的解决...
  7. python for while enumerate
  8. Latex 修改公式的的大小
  9. 每周进步要点(第50周12.4-12.11)
  10. vs code vue 语法提示不全_Vue造轮子必备*.vue文件源码读取并高亮展示
  11. 加密解密技术基础、PKI及创建私有CA
  12. asp.net membership 配置错误
  13. 大型网站的系统架构(摘)
  14. vscode创建工作区及生成html模板
  15. java读取.doc文件
  16. Android Studio 模拟器Emulator 设置代理网络 | WIFI图标有叉号但是有网络解决方案
  17. 异常检测综述(Anomaly Detection: A Survey)
  18. ftp服务器怎样批量删除文件,ftp地址不能从快速访问中删除,其他的文件夹可以...
  19. 非常详尽,多图慎入:Wayland与Weston简介
  20. QueryWrapper常用方法

热门文章

  1. 阿里云maven镜像库配置(gradle,maven)
  2. Process类详解
  3. 如何用计算机绘图画图,如何实现快速高效的计算机绘图
  4. kali无法进入gnome桌面环境
  5. 前端行为日志技术架构漫谈
  6. java struts 介绍_Struts简介、原理及简单实现
  7. 【STM32F4教程】第六节:通用定时器之PWM实现呼吸灯
  8. EVPN 路由之TYPE3
  9. 2020-2021 Android 腾讯面试真题含参考答案,写得太好了
  10. 快速阅读芯片datasheet