Java线程—-Runnable和Callable的区别和联系
Java 提供了三种创建线程的方法
1.继承Thread接口
public class Thread2Thread {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread1(), "线程2").start();
}
} /**
* 通过继承Thread类
*/
class MyThread1 extends Thread {
/**
* 重写run方法
*/
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
}
}
通过继承Thread类
2.实现Runnable接口
package com.testthread.demo4; import java.util.concurrent.ExecutorService; import static java.util.concurrent.Executors.*; public class Thread2Runnable { public static void main(String[] args) { //case1:通过实现Runnable接口,来实现run方法的具体逻辑
new Thread(new MyThread2(), "线程1").start();
//case2:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub }
}, "线程2").start(); //其实case1和case2的本质是一样的 //case3:作为线程任务提交给线程池,通过线程池维护的工作者线程来执行。
ExecutorService executor = newCachedThreadPool();
MyThread2 myThread2 = new MyThread2();
executor.execute(myThread2);
executor.shutdown();
}
} /**
* 实现Runnable接口的线程类
*/
class MyThread2 implements Runnable { /**
* 重写run方法
*/
@Override
public void run() {
// TODO Auto-generated method stub
}
}
实现Runnable接口
3.通过Callable和Future创建线程
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask; public class Thread2Callable {
public static void main(String[] args) {
//创建 Callable 实现类的实例
MyCallable myCallable = new MyCallable();
//使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值
FutureTask<String> futureTask = new FutureTask<String>(myCallable);
String res = null;
try {
//使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程
//没这句,下句代码获取不到结果,会一直等待执行结果
new Thread(futureTask,"线程1").start();
//调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
res = futureTask.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(res);
}
}
/**
* 创建 Callable 接口的实现类,并实现 call() 方法
*/
class MyCallable implements Callable<String> { /**
* 该 call() 方法将作为线程执行体,并且有返回值
*/
@Override
public String call() throws Exception {
return "success";
}
}
通过Callable和Future创建线程
Runnable和Callable的区别和联系
接口定义
Runnable
其中Runnable应该是我们最熟悉的接口,它只有一个run()函数,用于将耗时操作写在其中,该函数没有返回值。然后使用某个线程去执行该runnable即可实现多线程,Thread类在调用start()函数后就是执行的是Runnable的run()函数。
Runnable的声明如下 :
public interface Runnable {
/*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Runnable
#Callable
Callable与Runnable的功能大致相似,Callable中有一个call()函数,但是call()函数有返回值,而Runnable的run()函数不能将结果返回给客户程序。
Callable的声明如下 :
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
#Future
Executor就是Runnable和Callable的调度容器,Future就是对于具体的Runnable或者Callable任务的执行结果进行
取消、查询是否完成、获取结果、设置结果操作。get方法会阻塞,直到任务返回结果(Future简介)。
Future声明如下 :
public interface Future<V> { /**
* Attempts to cancel execution of this task. This attempt will
* fail if the task has already completed, has already been cancelled,
* or could not be cancelled for some other reason. If successful,
* and this task has not started when <tt>cancel</tt> is called,
* this task should never run. If the task has already started,
* then the <tt>mayInterruptIfRunning</tt> parameter determines
* whether the thread executing this task should be interrupted in
* an attempt to stop the task.
*/
boolean cancel(boolean mayInterruptIfRunning); /**
* Returns <tt>true</tt> if this task was cancelled before it completed
* normally.
*/
boolean isCancelled(); /**
* Returns <tt>true</tt> if this task completed.
*
*/
boolean isDone(); /**
* Waits if necessary for the computation to complete, and then
* retrieves its result.
*
* @return the computed result
*/
V get() throws InterruptedException, ExecutionException; /**
* Waits if necessary for at most the given time for the computation
* to complete, and then retrieves its result, if available.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
* @return the computed result
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future
#FutureTask(很有用)
FutureTask是一个RunnableFuture<V>
public class FutureTask<V> implements RunnableFuture<V>
FutureTask
RunnableFuture实现了Runnbale又实现了Futrue<V>这两个接口
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
RunnableFuture
另外FutureTaslk还可以包装Runnable和Callable<V>, 由构造函数注入依赖。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
} public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
FutureTask(callable)
上面代码块可以看出:Runnable注入会被Executors.callable()函数转换为Callable类型,即FutureTask最终都是执行Callable类型的任务。
该适配函数的实现如下 :
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
callable
RunnableAdapter适配器
/**
* A callable that runs given task and returns given result
*/
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
RunnableAdapter
FutureTask实现Runnable,所以能通过Thread包装执行, FutureTask实现Runnable,所以能通过提交给ExcecuteService来执行 注:ExecuteService:创建线程池实例对象,其中有submit(Runnable)、submit(Callable)方法 ExecturService:https://blog.csdn.net/suifeng3051/article/details/49443835 还可以直接通过get()函数获取执行结果,该函数会阻塞,直到结果返回。 因此FutureTask是Future也是Runnable,又是包装了的Callable( 如果是Runnable最终也会被转换为Callable )。
相同点
都是接口
都可以编写多线程程序
都采用Thread.start()启动线程
不同点
Callable规定的方法是call(),而Runnable规定的方法是run().
Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
call()方法可抛出异常,而run()方法是不能抛出异常的。--run()方法异常只能在内部消化,不能往上继续抛
运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
示例:
package com.xzf.callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; public class RunnableFutureTask {
static ExecutorService executorService = Executors.newSingleThreadExecutor(); //创建一个单线程执行器
public static void main(String[] args) {
runnableDemo();
futureDemo();
}
/**
* new Thread(Runnable arg0).start(); 用Thread()方法开启一个新线程
* runnable, 无返回值
*/
static void runnableDemo() {
new Thread(new Runnable() {
public void run() {
System.out.println("runnable demo:" + fibc(20)); //有值
} }).start();
}
/**
* Runnable实现的是void run()方法,无返回值
* Callable实现的是 V call()方法,并且可以返回执行结果
* Runnable可以提交给Thread,在包装下直接启动一个线程来执行
* Callable一般都是提交给ExecuteService来执行
*/ static void futureDemo() {
try {
Future<?> result1 = executorService.submit(new Runnable() {
public void run() {
fibc(20);
}
});
System.out.println("future result from runnable:"+result1.get()); //run()无返回值所以为空,result1.get()方法会阻塞
Future<Integer> result2 = executorService.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return fibc(20);
}
});
System.out.println("future result from callable:"+result2.get()); //call()有返回值,result2.get()方法会阻塞
FutureTask<Integer> result3 = new FutureTask<Integer>(new Callable<Integer>() {
public Integer call() throws Exception {
return fibc(20);
}
});
executorService.submit(result3);
System.out.println("future result from FutureTask:" + result3.get()); //call()有返回值,result3.get()方法会阻塞 /*因为FutureTask实现了Runnable,因此它既可以通过Thread包装来直接执行,也可以提交给ExecuteService来执行*/
FutureTask<Integer> result4 = new FutureTask<Integer>(new Runnable() {
public void run() {
fibc(20);
}
},fibc(20));
executorService.submit(result4);
System.out.println("future result from executeService FutureTask :" + result4.get()); //call()有返回值,result3.get()方法会阻塞
//这里解释一下什么FutureTask实现了Runnable结果不为null,这就用到FutureTask对Runnable的包装,所以Runnable注入会被Executors.callable()函数转换成Callable类型 FutureTask<Integer> result5 = new FutureTask<Integer>(new Runnable() {
public void run() {
fibc(20);
}
},fibc(20));
new Thread(result5).start();
System.out.println("future result from Thread FutureTask :" + result5.get()); //call()有返回值,result5.get()方法会阻塞 } catch (Exception e) {
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
static int fibc(int num) {
if (num==0) {
return 0;
}
if (num==1) {
return 1;
}
return fibc(num-1) + fibc(num-2);
}
}
示例1
运行结果:
runnable demo:6765
future result from runnable:null
future result from callable:6765
future result from FutureTask:6765
future result from executeService FutureTask :6765
future result from Thread FutureTask :6765
运行结果1
package com.testthread.test; import java.util.concurrent.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList; public class Test implements Callable<Object> {
private String taskNum; Test(String taskNum) {
this.taskNum = taskNum;
} public static void main(String[] args) {
System.out.println("----程序开始运行----");
Date date1 = new Date();
int taskSize = 5; // 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize); // 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
try {
for (int i = 0; i < taskSize; i++) {
Callable c = new Test(i + " "); // 执行任务并获取Future对象
Future f = pool.submit(c);
list.add(f);
}
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从Future对象上获取任务的返回值,并输出到控制台
System.out.println(">>>" + f.get().toString());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {// 关闭线程池
pool.shutdown();
}
Date date2 = new Date();
System.out.println("----程序结束运行----,程序运行时间【" + (date2.getTime() - date1.getTime()) + "毫秒】");
} @Override
public Object call() throws Exception {
System.out.println(">>>" + taskNum + "任务启动");
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
System.out.println(">>>" + taskNum + "任务终止");
return taskNum + "任务返回运行结果,当前任务时间【" + time + "毫秒】";
}
}
示例2
----程序开始运行----
>>>1 任务启动
>>>0 任务启动
>>>3 任务启动
>>>2 任务启动
>>>4 任务启动
>>>1 任务终止
>>>3 任务终止
>>>0 任务终止
>>>0 任务返回运行结果,当前任务时间【1029毫秒】
>>>2 任务终止
>>>1 任务返回运行结果,当前任务时间【1029毫秒】
>>>2 任务返回运行结果,当前任务时间【1030毫秒】
>>>3 任务返回运行结果,当前任务时间【1030毫秒】
>>>4 任务终止
>>>4 任务返回运行结果,当前任务时间【1030毫秒】
----程序结束运行----,程序运行时间【1146毫秒】
示例2结果
package com.testthread.test; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.FutureTask; import static java.util.concurrent.Executors.newFixedThreadPool; public class Test2 { public static void main(String[] args) {
Map<String, Object> resultMap = new HashMap<>();
int count = 10;
ExecutorService executorService = newFixedThreadPool(10);
long start = System.currentTimeMillis();
try {
List<FutureTask> list = new ArrayList();
for (int i = 0; i < count; i++) {
FutureTask<Map<String, Object>> result = new FutureTask<Map<String, Object>>(myCall(i + ""));
executorService.submit(result);
list.add(result);
}
for (int i = 0; i < count; i++) {
Map<String, Object> resultMapShow = (Map<String, Object>) list.get(i).get();
System.out.println("resultMapShow = " + resultMapShow);
Map<String, Object> body = (Map<String, Object>) resultMapShow.get("body");
resultMap.put("aa" + i, body.get("aa"));
}
System.out.println("====>took:" + (System.currentTimeMillis() - start)); } catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
System.out.println("resultMap = " + resultMap);
System.out.println("==>took:" + (System.currentTimeMillis() - start));
} public static Callable<Map<String, Object>> myCall(String taskId) {
Callable<Map<String, Object>> callable = new Callable<Map<String, Object>>() {
@Override
public Map<String, Object> call() throws Exception {
return queryMethod(taskId);
}
};
return callable;
} private static Map<String, Object> queryMethod(String taskId) {
try {
System.out.println(" ==>任务启动" + taskId);
long startI = System.currentTimeMillis();
Thread.sleep(500);
// System.out.println(" sleep:500ms");
System.out.println(" ==>任务终止" + taskId + " 任务时间:" + (System.currentTimeMillis() - startI));
} catch (InterruptedException e) {
e.printStackTrace();
}
Map<String, Object> resultMap = new HashMap<>();
Map<String, Object> head = new HashMap<>();
head.put("retFlag", "0000");
head.put("retMsg", "成功");
Map<String, Object> body = new HashMap<>();
body.put("aa", "11");
resultMap.put("head", head);
resultMap.put("body", body);
return resultMap;
}
}
示例3
==>任务启动0
==>任务启动1
==>任务启动2
==>任务启动3
==>任务启动4
==>任务启动5
==>任务启动6
==>任务启动7
==>任务启动8
==>任务启动9
==>任务终止0 任务时间:501
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
==>任务终止4 任务时间:502
==>任务终止3 任务时间:502
==>任务终止2 任务时间:502
==>任务终止1 任务时间:502
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
==>任务终止6 任务时间:502
==>任务终止5 任务时间:502
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
==>任务终止8 任务时间:501
==>任务终止7 任务时间:501
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
==>任务终止9 任务时间:501
resultMapShow = {head={retMsg=成功, retFlag=0000}, body={aa=11}}
====>took:525
resultMap = {aa1=11, aa0=11, aa3=11, aa2=11, aa5=11, aa4=11, aa7=11, aa6=11, aa9=11, aa8=11}
==>took:526
示例3结果
关系图:
下面是关系图,望有助理解
转自:https://blog.csdn.net/sinat_39634657/article/details/81456810
https://blog.csdn.net/u012894692/article/details/80215140
https://blog.csdn.net/rexueqingchun/article/details/79025882
Java线程—-Runnable和Callable的区别和联系的更多相关文章
- java中Runnable和Callable的区别
文章目录 运行机制 返回值的不同 Exception处理 java中Runnable和Callable的区别 在java的多线程开发中Runnable一直以来都是多线程的核心,而Callable是ja ...
- Runnable和Callable 的区别
Runnable和Callable 的区别 01.Runnable接口中只有一个run()没有返回值 没有声明异常 Callable接口中只有一个call()有返回值 有声明异常 02.Calla ...
- Runnable 和 Callable的区别
Runnable 与 Callable的区别: (1)Callable规定的方法是call(),Runnable规定的方法是run(). (2)Callable的任务执行后可返回值,而Runnable ...
- Java线程池(Callable+Future模式)
转: Java线程池(Callable+Future模式) Java线程池(Callable+Future模式) Java通过Executors提供四种线程池 1)newCachedThreadPoo ...
- Java多线程Runnable与Callable区别与拓展
我们先来分别看一下这两个接口 Runnable: // // Source code recreated from a .class file by IntelliJ IDEA // (powered ...
- java线程——详解Callable、Future和FutureTask
回顾: 接上篇博客 java线程--三种创建线程的方式,这篇博客主要介绍第三种方式Callable和Future.比较继承Thread类和实现Runnable接口,接口更加灵活,使用更广泛.但这两种方 ...
- JAVA 线程池之Callable返回结果
本文介绍如何向线程池提交任务,并获得任务的执行结果.然后模拟 线程池中的线程在执行任务的过程中抛出异常时,该如何处理. 一,执行具体任务的线程类 要想 获得 线程的执行结果,需实现Callable接口 ...
- Java并发-Runnable、Callable、Future、Future Task
Runnable: Runnable的代码非常简单,他是一个接口,且接口中只有一个方法,run(),创建一个类实现他,把一些费时操作写在其中,然后使用某个线程去执行该Runnable实现类即可实现多线 ...
- 浅谈线程runnable和callable的使用及区别
线程使用比较广泛,但实际上一般项目很少用上线程,线程常用于优化复杂的程序执行流程,把一些与业务关系关系不大但是必须要执行的流程使用线程的方式让子线程去执行,主流程只返回跟业务有关的信息 runnabl ...
随机推荐
- [BZOJ3920]Yuuna的礼物
题目大意: 给你一个长度为$n(n\le40000)$的数列$\{a_i\}(1\le a_i\le n)$,给出$m(m\le40000)$次询问,每次给出$l,r,k_1,k_2$询问区间$[l, ...
- [JZOJ3105]拼图
题目大意: 给你一个起始串$a(|a|\leq 300)$,一个目标串$b(|b|\leq 300)$,以及$n(n\leq 8)$个小串$s_0,s_2,\ldots,s_{n-1}(|s_i|\ ...
- cocurrent包countdownlatch 倒计时门栓
latch 英[lætʃ]美[lætʃ]n. 门闩; 弹簧锁; 锁是每个类的成员变量,它是这个类的固有属性,当然要声明为成员变量. 成员变量的初始化是通过对象的构造函数的. 锁是每个类的成员变量,它是 ...
- 【Linux】linux命令大全
[注意]:命令[compgen -b]可以列出所有当前系统支持的命令. 109个Linux命令 目录 1 文件管理... 5 1.1 basename. 5 1.2 ...
- Python 面向对象二(转载)
来源:www.cnblogs.com/wupeiqi/p/4766801.html 三.类成员的修饰符 类的所有成员在上一步骤中已经做了详细的介绍,对于每一个类的成员而言都有两种形式: 1.公有成员, ...
- hadoop错误总结
1.hadoop3: mkdir: cannot create directory `/usr/local/hadoop/bin/../logs': Permission denied 把所有Data ...
- 完全分布式安装hadoop
以三个节点为例的服务器集群来安装和配置hadoop 以下是各服务器ip地址和对应所做的节点 192.168.61.128 master 192.168.61.129 slave1 192.168.61 ...
- Elasticsearch教程(一),全程直播(小白级别)
ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java开发的,并作为Apach ...
- EffectiveJava(1) 构造器和静态工厂方法
构造器和静态工厂方法 **构造器是大家创建类时的构造方法,即使不显式声明,它也会在类内部隐式声明,使我们可以通过类名New一个实例. 静态方法是构造器的另一种表现形式** 主题要点:何时以及如何创建对 ...
- html中插入php的方法
.html文件是不可以被读取为php文件的必须修改后缀名为.php这个时候如果你在浏览器中调用此页面所有的HTML代码都会被正确显示 这个时候你可以在文件的任意为止插入<?php ?>作为 ...