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

时间:2022-07-26
本文章向大家介绍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