知识补充:多线程的第三种方式

来源:http://www.threadworld.cn/archives/39.html

创建线程的两种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。这两种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

一、Runnable接口

先看一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

二、Callable接口

Callable接口位于java.util.concurrent包下,在它里面也只声明了一个方法,只不过这个方法叫做call()。

可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。Callable接口可以看作是Runnable接口的补充,call方法带有返回值,并且可以抛出异常。

三、FutureTask类

如何获取Callable的返回结果呢?一般是通过FutureTask这个中间媒介来实现的。整体的流程是这样的:
把Callable实例当作参数,生成一个FutureTask的对象,然后把这个对象当作一个Runnable,作为参数另起线程。

3.1 FutureTask的结构

3.2 FutureTask的启动
由于FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行

四、Future接口

FutureTask继承体系中的核心接口是Future。Future的核心思想是:一个方法,计算过程可能非常耗时,等待方法返回,显然不明智。可以在调用方法的时候,立马返回一个Future,可以通过Future这个数据结构去控制方法f的计算过程。
这里的控制包括:
get方法:获取计算结果(如果还没计算完,也是必须等待的)
cancel方法:还没计算完,可以取消计算过程
isDone方法:判断是否计算完
isCancelled方法:判断计算是否被取消

例子

  1. public class ThreadMain {
  2. public static void main(String[] args) throws Exception, ExecutionException {
  3. ThreadTest threadTest = new ThreadTest();//线程任务
  4. // FutureTask接收线程的返回数据
  5. FutureTask<String> f = new FutureTask<>(threadTest);
  6. Thread thread = new Thread(f);
  7. thread.start();
  8. String res = f.get();
  9. System.out.println(res);
  10. }
  11. }
  12.  
  13. class ThreadTest implements Callable<String>{
  14. @Override
  15. public String call() throws Exception {
  16. return "hello";
  17. }
  18. }

一. 单词统计案例

使用多线程,不同的线程统计不同的文件中的单词的个数,将统计的结果数据写入到指定的中间文件中进行单词个数的汇总统计 。

客户端:

  1. public class MyClient implements Callable<String>{
  2.  
  3. //不同线程处理不同的文件
  4. String localFilePath ;
  5. public MyClient(String localFilePath) {
  6. super();
  7. this.localFilePath = localFilePath;
  8. }
  9.  
  10. public static final String KEY = "天王盖地虎";
  11. public static final String PATH = "E:/javafile/wc_server.jar";
  12. @SuppressWarnings("unused")
  13. private String task() throws Exception{
  14. String res = null;
  15. Socket socket = new Socket("127.0.0.1",8888);
  16. ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
  17. ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
  18. //1 发送身份校验
  19. oos.writeObject(KEY);
  20. //2 接收校验结果
  21. boolean b = (boolean)ois.readObject();
  22. if(b) {//校验成功
  23. System.out.println("校验成功");
  24. //3 发送服务端存储jar包的路径
  25. oos.writeObject(PATH);
  26. //4 往服务端发送jar包
  27. // 4.1 读取本地需要的jar包
  28. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:/javafile/wc.jar"));
  29. byte[] bs = new byte[1024];
  30. int len;
  31. while((len = bis.read(bs))!= -1) {
  32. // 4.2 利用网络流发送此jar包
  33. oos.write(bs,0,len);
  34. }
  35. oos.flush();
  36. bis.close();
  37. // 往服务端发送执行命令
  38. String cmd = "java -jar "+PATH+" " +localFilePath;
  39. oos.writeObject(cmd);
  40. // 接收服务端jar程序执行的结果
  41. res = (String)ois.readObject();
  42. // System.out.println(res);
  43. }else {
  44. System.out.println("校验失败");
  45. }
  46. socket.close();
  47. return res;
  48. }
  49. @Override
  50. public String call() throws Exception {
  51. String res = task();
  52. return res;
  53. }
  54. }

服务端

  1. public class MyServer {
  2. public static final String KEY = "天王盖地虎";
  3. public static void main(String[] args) throws Exception {
  4. ServerSocket ss = new ServerSocket(8888);
  5. while(true) {
  6. Socket socket = ss.accept();
  7. OutputStream os = socket.getOutputStream();
  8. InputStream in = socket.getInputStream();
  9. ObjectInputStream ois = new ObjectInputStream(in);
  10. ObjectOutputStream oos = new ObjectOutputStream(os);
  11. //接收客户端发送的身份校验
  12. String str = (String)ois.readObject();
  13. //校验成功
  14. if(KEY.equals(str)) {
  15. oos.writeObject(true);
  16. // 接收路径
  17. String path = (String)ois.readObject();
  18. // 接收jar包,并写入指定的路径
  19. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));
  20. byte[] bs = new byte[1024];
  21. int len = 0;
  22. while((len = ois.read(bs)) != -1) {
  23. bos.write(bs,0,len);
  24. }
  25. bos.flush();
  26. bos.close();
  27. // 接收执行命令
  28. String cmd = (String)ois.readObject();
  29. Runtime runtime = Runtime.getRuntime();
  30. Process p = runtime.exec(cmd);
  31. InputStream inputStream = p.getInputStream();
  32. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
  33. String line = null;
  34. StringBuilder res = new StringBuilder();
  35. while((line = br.readLine()) != null) {
  36. res.append(line+"\r\n");
  37. }
  38. // 将结果返回给客户端
  39. oos.writeObject(res.toString());
  40. oos.flush();
  41. // 校验失败
  42. }else {
  43. //将结果返回到客户端
  44. oos.writeObject(false);
  45. }
  46. }
  47. }
  48. }

多线程统计结果

  1. public class TestClient {
  2. public static void main(String[] args) throws Exception {
  3. // 创建第一个线程任务
  4. MyClient m1 = new MyClient("E:/javafile/wc1.txt");
  5. FutureTask<String> f1 = new FutureTask<>(m1);
  6. new Thread(f1).start();
  7. String res1 = f1.get();
  8. // System.out.println("线程1的结果:"+"/r/n"+ res1);
  9.  
  10. // 创建第二个线程任务
  11. MyClient m2 = new MyClient("E:/javafile/wc2.txt");
  12. FutureTask<String> f2 = new FutureTask<>(m2);
  13. new Thread(f2).start();
  14. String res2 = f2.get();
  15. // System.out.println("线程2的结果:" +res2);
  16. //结果数据的汇总 ---> 写到中间结果
  17. //将结果数据追加到指定的文件中
  18. BufferedWriter bw = new BufferedWriter(new FileWriter(new File("E:/javafile/res.res"), true));
  19. bw.write(res1);
  20. bw.write(res2, 0, res2.length()-2);
  21. //bw.write(res2);
  22. bw.close();
  23. //统计结果数据
  24. Map<String,Integer> map = new HashMap<>() ;
  25. BufferedReader br = new BufferedReader(new FileReader("E:/javafile/res.res"));
  26. String line = null ;
  27. while((line = br.readLine())!=null){
  28. System.out.println(line);
  29. String[] split = line.split(":");
  30. String word = split[0];
  31. int num = Integer.parseInt(split[1]);
  32. Integer num2 = map.getOrDefault(word, 0);
  33. num2+=num;
  34. map.put(word, num2);
  35. }
  36. Set<Entry<String, Integer>> entrySet = map.entrySet();
  37. for (Entry<String, Integer> entry : entrySet) {
  38. System.out.println(entry);
  39. }
  40. }
  41. }

零基础学习java------30---------wordCount案例(涉及到第三种多线程callable)的更多相关文章

  1. 总结了零基础学习Java编程语言的几个基础知识要点

    很多Java编程初学者在刚接触Java语言程序的时候,不知道该学习掌握哪些必要的基础知识.本文总结了零基础学习Java编程语言的几个基础知识要点. 1先了解什么是Java的四个方面   初学者先弄清这 ...

  2. 音乐出身的妹纸,零基础学习JAVA靠谱么

    问:表示音乐出身的妹纸一枚  某一天突然觉得身边认识的是一群程序员   突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Ja ...

  3. java核心知识点----创建线程的第三种方式 Callable 和 Future CompletionService

    前面已经指出通过实现Runnable时,Thread类的作用就是将run()方法包装成线程执行体,那么是否可以直接把任意方法都包装成线程执行体呢?Java目前不行,但其模仿者C#中是可以的. Call ...

  4. 零基础学习java------31---------共享单车案例,html快速入门(常见标签,get和post的区别)

     一 .单车案例 二. HTML快速入门 红字表示要掌握的内容 超文本标记语言,此处的标记指的即是关键字,其用处是用来写页面(展示数据). 语法:(1)./当前目录:../ 父级目录 (2)注释符号: ...

  5. 零基础学习hadoop到上手工作线路指导(编程篇)

    问题导读: 1.hadoop编程需要哪些基础? 2.hadoop编程需要注意哪些问题? 3.如何创建mapreduce程序及其包含几部分? 4.如何远程连接eclipse,可能会遇到什么问题? 5.如 ...

  6. 零基础学习hadoop到上手工作线路指导(中级篇)

    此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结. 五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解: hadoop分为h ...

  7. 零基础学习hadoop到上手工作线路指导初级篇:hive及mapreduce

      此篇是在零基础学习hadoop到上手工作线路指导(初级篇)的基础,一个继续总结.五一假期:在写点内容,也算是总结.上面我们会了基本的编程,我们需要对hadoop有一个更深的理解:hadoop分为h ...

  8. 零基础学Java第一节(语法格式、数据类型)

    本篇文章是<零基础学Java>专栏的第一篇文章,从本篇文章开始,将会连更本专栏,带领大家将Java基础知识彻底学懂,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! ...

  9. 零基础学Java第五节(面向对象一)

    本篇文章是<零基础学Java>专栏的第五篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] 类与对象 在哲学体系中,可以分为主 ...

随机推荐

  1. JAVA笔记14__多线程共享数据(同步)/ 线程死锁 / 生产者与消费者应用案例 / 线程池

    /** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两 ...

  2. 理解ASP.NET Core - 文件服务器(File Server)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 提供静态文件 静态文件默认存放在 Web根目录(Web Root) 中,路径为 项目根目录(C ...

  3. 关于docker中容器可以Ping通外网,真机无法Ping通容器的问题

    首先我们要知道整体的框架结构,docker是我们安装在centos7上的,而centos7是安装在vmware上.其中docker中还有若干容器运行. 整体框架图如下: 我们将它分为两部分,一部分是d ...

  4. SpringCloud 2020.0.4 系列之 Gateway入门

    1. 概述 老话说的好:做人要有幽默感,懂得幽默的人才会活的更开心. 言归正传,今天我们来聊聊 SpringCloud 的网关组件 Gateway,之前我们去访问 SpringCloud 不同服务的接 ...

  5. 倒谱Cepstrum本质的理解

    1.理解: 信号叠加时,不是都是线性关系(时域相互+ 频率相加):有的时候是两种信号成分相乘得到的,(时域卷积,频域相乘):比如很多齿轮啮合时振动信号调制现象,电机的轴向与径向的振动耦合时采集到的振动 ...

  6. 《手把手教你》系列技巧篇(三十九)-java+ selenium自动化测试-JavaScript的调用执行-上篇(详解教程)

    1.简介 在做web自动化时,有些情况selenium的api无法完成,需要通过第三方手段比如js来完成实现,比如去改变某些元素对象的属性或者进行一些特殊的操作,本文将来讲解怎样来调用JavaScri ...

  7. Fiddler抓包工具简介:(四)Fiddler的基本使用

    Fiddler的使用 视图功能区域 会话的概念:一次请求和一次响应就是一个会话. fiddler主界面 下面挑几个快捷功能区中常用几项解释,其他功能自己尝试: 快捷功能区 1:给会话添加备注信息 2: ...

  8. centos7.2安装nginx

    1 安装相关编译环境 yum install gcc-c++ yum install pcre pcre-devel yum install zlib zlib-level yum openssl o ...

  9. 如何提高C# StringBuilder的性能

    本文探讨使用C# StringBuilder 的最佳实践,用于减少内存分配,提高字符串操作的性能. 在 .NET 中,字符串是不可变的类型.每当你在 .NET 中修改一个字符串对象时,就会在内存中创建 ...

  10. [gym102822I]Invaluable Assets

    令$f(x)=\frac{x^{2}+c}{x}$,换言之即$x$物品的性价比的倒数 对其求导即$f'(x)=1-\frac{c}{x^{2}}$,其导数严格递增,换言之即是一个严格下凸函数,记$x_ ...