专栏首页做不甩锅的后端Callable and Future in Java(java中的Callable和Future)

Callable and Future in Java(java中的Callable和Future)

需要Callable的理由

通常,我们有两种方式创建线程,一种方式是继承Thread类,另外一种方式是实现Runnable接口。然而,Runnable方式缺少的一个特性就是,当线程终止的时候,即run运行完成的时候,我们布恩那个让线程返回一个执行结果。为了之处这个特性,在java中就增加了Callable接口。

Callable vs Runnable

  • 为了实现Runnable接口,需要实现不返回任何返回值的run方法,而对于callable,我们需要实现在完成的时候,返回接口的call方法,注意,线程不能用Callable创建,只能用Runnbale的方式创建。
  • 另外一个区别就是,call方法可以抛出异常。而run方法则不能。
public Object call() throws Exception;

如下是Callable的代码示例,它将在大约0-4秒之后返回一个随机数:

public class CallableExample implements Callable {

	@Override
	public Object call() throws Exception {
		Random generator = new Random();
		Integer randomNumber = generator.nextInt(5);
		TimeUnit.SECONDS.sleep(randomNumber);

		return randomNumber;
	}
	
}

Future

当call方法执行完成的时候,计算结果必须存储在main线程的已知对象中,以便mian线程可以值得这个call线程的返回结果,但是程序在此后将如何存储和获得这个结果呢? 为此,我们需要使用Future丢箱,可以将Future看作一个持有结果的对象,它可能现在不持有结果,但是将来,一旦Callable执行完成,就会这样做,因此,Futrue基本上是利用主线程跟踪其他线程结果的一种方式,要实现这个接口,需要重写5个方法,以下示例是其具体实现,在此,我们只列出了重要的方法。 需要注意的是,Callable和Future做了两件不同的事情,Callable和Runnable类似,因为它封装了一个任务,该任务在另外一个线程上运行,而Future用于存储从另外一个线程获得的结果,事实上,未来也可以使用Runnable,这一点在Executors参与之后就会变得很清晰。

  • public boolean cancel(boolean mayInterrupt): 用于停止任务,如果任务尚未启动,他将停止该任务,如果任务已经启动,则在manInteerrupt为ture的时候中断该任务。
  • public Object get() throws InterruptedException, ExecutionException:用于获得任务的结果,如果任务完成,则立即返回结果,否则等待任务完成,然后返回结果。
  • public boolean isDone():如果任务完成,则返回true,否则,返回false。

如果需要创建线程,那么细羽一个Runnable,如果需要取得结果,那么需要一个Future。 在java中,具体的类似是FutureTask,它实现了Runnable和Future,方便地结合了这两种功能。 FutureTask可以通过为其提供的构造函数来创建Callable,然后将FutureTask对象提供给Thread的构造函数来创建Thread对象。因此,间接的实现了Callable创建线程。在此要重点强调的是,没有办法直接用Callable创建线程。 如下是Callable和FutureTask的完整示例:

public class CallableExample implements Callable {

	@Override
	public Object call() throws Exception {
		Random generator = new Random();
		Integer randomNumber = generator.nextInt(5);
		TimeUnit.SECONDS.sleep(randomNumber);

		return randomNumber;
	}

}

public class CallableFutureTest {
	public static void main(String[] args) throws Exception
	{

		// FutureTask is a concrete class that
		// implements both Runnable and Future
		FutureTask[] randomNumberTasks = new FutureTask[5];

		for (int i = 0; i < 5; i++)
		{
			Callable callable = new CallableExample();

			// Create the FutureTask with Callable
			randomNumberTasks[i] = new FutureTask(callable);

			// As it implements Runnable, create Thread
			// with FutureTask
			Thread t = new Thread(randomNumberTasks[i]);
			t.start();
		}

		for (int i = 0; i < 5; i++)
		{
			// As it implements Future, we can call get()
			System.out.println(randomNumberTasks[i].get());

			// This method blocks till the result is obtained
			// The get method can throw checked exceptions
			// like when it is interrupted. This is the reason
			// for adding the throws clause to main
		}
	}
}

执行结果:

2
2
3
4
0

线程启动后与它所有的交互都使用FutureTask对象来实现Future接口,因此,不需要存储线程对象,使用TutureTask对象,可以实现对task的取消,以及检查它释放完成或者尝试获得其执行结果。 如下是通过Runnable实现类型功能的代码:

public class RunnableExample implements Runnable {

	private Object result = null;

	@Override
	public void run() {
		Random generator = new Random();
		Integer randomNumber = generator.nextInt(5);

		// As run cannot throw any Exception
		try {
			Thread.sleep(randomNumber * 1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		// Store the return value in result when done
		result = randomNumber;

		// Wake up threads blocked on the get() method
		synchronized (this) {
			notifyAll();
		}
	}

	public synchronized Object get()
			throws InterruptedException {
		while (result == null) {
			wait();
		}

		return result;
	}
}

public class RunnableTest {

	public static void main(String[] args) throws Exception {
		RunnableExample[] randomNumberTasks = new RunnableExample[5];

		for (int i = 0; i < 5; i++) {
			randomNumberTasks[i] = new RunnableExample();
			Thread t = new Thread(randomNumberTasks[i]);
			t.start();
		}

		for (int i = 0; i < 5; i++) {
			System.out.println(randomNumberTasks[i].get());
		}
	}
}

输出:

4
4
0
2
3

参考

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/FutureTask.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • java线程池(一):java线程池基本使用及Executors

    在前面学习线程组的时候就提到过线程池。实际上线程组在我们的日常工作中已经不太会用到,但是线程池恰恰相反,是我们日常工作中必不可少的工具之一。现在开始对线...

    冬天里的懒猫
  • java线程池(二):聊聊newFixedThreadPool(1)和newSingleThreadExecutor()的区别

    在第一部分中介绍完java中Executors的线程池创建的方式之后,实际上有一个非常好奇的问题。既然newFixedThreadPool(1)也能保证...

    冬天里的懒猫
  • 多线程基础(一): 线程概念及生命周期

    什么是进程,相信大家都知道什么是进程却很难解释清楚。百科中的解释是:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调...

    冬天里的懒猫
  • Java并发编程(08):Executor线程池框架

    Executor系统中,将线程任务提交和任务执行进行了解耦的设计,Executor有各种功能强大的实现类,提供便捷方式来提交任务并且获取任务执行结果,封装了任务...

    知了一笑
  • ConcurrentDictionary线程不安全么,你难道没疑惑,你难道弄懂了么?

    事情不太多时,会时不时去看项目中同事写的代码可以作个参考或者学习,个人觉得只有这样才能走的更远,抱着一副老子天下第一的态度最终只能是井底之蛙。前两篇写到关于断点...

    yaphetsfang
  • Java线程池:ExecutorService 的理解与使用

    接口 java.util.concurrent.ExecutorService 表述了异步执行的机制,并且可以让任务在后台执行。一个 ExecutorServi...

    Java编程指南
  • 搞懂这几个锁用法,多线程就懂一半了

    synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的。

    java乐园
  • 菜鸟的进阶之路:了解使用多线程

    关于多进程和多线程,教科书上最经典的一句话是“进程是资源分配的最小单位,线程是CPU调度的最小单位”,一般来说进程是独立的而同一进程中的线程是共享的,但是开一个...

    码农小胖哥
  • 学习使用Lock+Conditionk编写三个经典多线程例子

    在jdk5之后的高级并发包里面Lock接口可以替换原来jvm内置的锁synchronized关键字,同理使用Condition接口的await,signal,s...

    我是攻城师
  • 对象共享:Java并发环境中的烦心事

    并发的意义在于多线程协作完成某项任务,而线程的协作就不可避免地需要共享数据。今天我们就来讨论下如何发布和共享类对象,使其可以被多个线程安全地访问。

    lyb-geek

扫码关注云+社区

领取腾讯云代金券