Future<V>接口Callable<V>接口的使用

本文介绍了Java并发编程中Future与Callable接口的应用,包括如何利用Future获取异步任务结果,以及如何通过CompletionService处理多个异步任务,确保按完成顺序处理结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在并发处理数据时,一个典型的场景是: 我们将需要处理的并且非常耗时的任务(例如访问网络IO)交给线程池中的一个线程去处理,我们当前的线程执行其他的任务,然后当交给线程池的任务执行完成后,我们再获取执行的结果,对得到的结果进处理,首选会充分利用CPU的多核资源,其次是将会阻塞的任务提前进行提交处理,尽量减少系统卡顿。(在互联网公司这样网络访问的方式非常常见。) 那么今天我们来谈一种新的实现方式Future<V>接口Callable<V>接口

Future<V>接口Callable<V>

在Future接口中声明了5个方法,下面依次解释每个方法的作用:

  1. cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
  2. isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  3. isDone方法表示任务是否已经完成,若任务完成,则返回true;
  4. get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  5. get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。 在callable接口中声明了1个方法,call()方法,主要用于执行你的操作,并放回操作后的值。

在callable接口中声明了1个方法,call()方法,主要用于执行你的操作,并放回操作后的值。 举个栗子 Future接口和Callable接口经常配合使用,废话不多说,上代码。

场景一

该场景只是简单的对Future接口和Callable接口进行使用介绍,注意Future接口的get()方法会阻塞,直到线程池中的线程将数据处理完成,才执行后面的操作。

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

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

		ExecutorService executor = Executors.newSingleThreadExecutor();// 单线程,线程池
		Future<Long> myFuturetask = executor.submit(new MyFutureTask());// 提交任务
		System.out.println("get before");
		long result = myFuturetask.get();// 这个地方会阻塞
		System.out.println("get after");
		System.out.println("get result is " + result);
	}
}

class MyFutureTask implements Callable<Long>
{

	[@Override](https://my.oschina.net/u/1162528)
	public Long call() throws Exception
	{
		long result = 1;

		System.out.println("future task start");

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)//计算阶乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(10);
		System.out.println("future task end");
		return result;
	}
}

执行结果

get before
future task start
future task end
get after
get result is 720

场景二

现在有这样一个需求,就是一下子提交很多的FutureTask到线程池中,然后我等待处理的结果,此时我要做的是:哪个FutureTask先处理完成,我就先处理其得到的结果。问题的难度在于:每个提交到线程池中的FutureTask处理的时间都是不一样的,我怎么来的得知那个FutureTask线程先处理完呢? 题外话:仔细想想:用Join()、CountDownLatch类、CyclicBarrier类是不是感觉都不太好实现?考虑了一下直接Runnable接口实现呢,是不是也会很麻烦?!后续会给出对比~ 废话不多说,直接上代码:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

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

		ExecutorService executor = Executors.newCachedThreadPool();// 多个线程的线程池
		List<Future<MyResult>> list = new ArrayList<Future<MyResult>>();
		for (int i = 1; i <= 6; i++)
		{
			list.add(executor.submit(new MyFutureTask(String.valueOf(i))));//提交任务
		}

		System.out.println("do your something");
		TimeUnit.SECONDS.sleep(3);

		int rank = 1;
		while (true)//一直访问,直到task 列表中的task 为零
		{
			if (list.size() <= 0)
			{
				break;
			}
			Iterator<Future<MyResult>> iterator = list.iterator();
			while (iterator.hasNext())//循环访问task
			{
				Future<MyResult> f = iterator.next();
				if (f.isDone())//task 是否完成,如果完成则获取值,如果
				{
					MyResult result = f.get();
					System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name
							+ " result is " + result.result);
					iterator.remove();
				}
			}
		}
	}
}

class MyFutureTask implements Callable<MyResult>
{

	private String name;

	public MyFutureTask(String name)
	{
		this.name = name;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public MyResult call() throws Exception
	{
		long result = 1;

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)// 计算阶乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(new Random().nextInt(10));
		System.out.println(name + " future task result " + result);

		return new MyResult(name, result);
	}
}

class MyResult
{
	String name;//记录任务ID
	long result;//结果

	public MyResult(String name, long result)
	{
		super();
		this.name = name;
		this.result = result;
	}

}

运行结果:

do your something
6 future task result 1
5 future task result 6
------------------>my rank is 1future task is 5 result is 6
------------------>my rank is 2future task is 6 result is 1
2 future task result 40320
------------------>my rank is 3future task is 2 result is 40320
1 future task result 6
4 future task result 720
------------------>my rank is 4future task is 1 result is 6
------------------>my rank is 5future task is 4 result is 720
3 future task result 24
------------------>my rank is 6future task is 3 result is 24

这个地方主要注意这个isDown()这个函数来判断提交的Future任务是否执行完成,如果完成就获取任务结果做后续的处理。 但是我们发现,存在一个问题就是:并没有真的像我想想的那样,哪个任务先执行完,我就先处理哪个,难道就真的没有办法了么?! 看下面代码:


import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FutrueTest
{

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

		ExecutorService executor = Executors.newCachedThreadPool();// 多个线程的线程池
		CompletionService<MyResult> completionService = new ExecutorCompletionService<MyResult>(executor);//主要是这个类

		for (int i = 1; i <= 6; i++)
		{
			completionService.submit(new MyFutureTask(String.valueOf(i)));// 提交任务
		}
		System.out.println("do your something");
		TimeUnit.SECONDS.sleep(3);//休眠,也可以不休眠
		int rank = 0;
		for (int i = 1; i <= 6; i++)
		{

			MyResult result = completionService.take().get();// 会阻塞哦

			System.out.println("------------------>my rank is " + rank++ + " future task is " + result.name
					+ " result is " + result.result);
		}

	}
}

class MyFutureTask implements Callable<MyResult>
{

	private String name;

	public MyFutureTask(String name)
	{
		this.name = name;
	}

	[@Override](https://my.oschina.net/u/1162528)
	public MyResult call() throws Exception
	{
		long result = 1;

		long max = new Random().nextInt(10);

		for (int i = 1; i <= max; i++)// 计算阶乘
		{
			result *= i;
		}
		TimeUnit.SECONDS.sleep(new Random().nextInt(10));
		System.out.println(name + " future task result " + result);

		return new MyResult(name, result);
	}
}

class MyResult
{
	String name;// 记录任务ID
	long result;// 结果

	public MyResult(String name, long result)
	{
		super();
		this.name = name;
		this.result = result;
	}

}

运行结果

do your something
3 future task result 24
1 future task result 6
4 future task result 1
------------------>my rank is 0 future task is 3 result is 24
------------------>my rank is 1 future task is 1 result is 6
------------------>my rank is 2 future task is 4 result is 1
5 future task result 720
6 future task result 40320
------------------>my rank is 3 future task is 5 result is 720
------------------>my rank is 4 future task is 6 result is 40320
2 future task result 2
------------------>my rank is 5 future task is 2 result is 2

终于得到了我们想要的结果。

总结 Callable接口和Runnable接口在某些情况下都可以提交一个自己的任务给线程池来执行。

区别在于:

1.Callable接口可以有返回值,而Runnable接口没有。(当然不是绝对的看你怎么实现)

2.Callable接口实现对象通过ExecutorService接口的submit()方法提交到线程池,而Runnable接口实现对象通过Executor接口的execute()方法提交大线程池,当然ExecutorService接口继承Executor接口

至于他们的使用,看场景

转载于:https://my.oschina.net/u/1473861/blog/1607462

package ajiayou.index0711.demo03; import java.util.Random; import java.util.concurrent.Callable; public class Match2 implements Callable<Integer> { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Integer call() throws Exception { Random r = new Random(); //这里是随机数 int speed = r.nextInt(100); int dist = 0; for(int i = 1; i<=100; i++) { //睡眠100毫秒 Thread.sleep(30); //Thread.currentThread().getName() //获得当前线程的名称 dist += i * speed; System.out.println(this.getName() + "已前进" + (dist) + "米(" + speed + "米/秒)"); } return dist; } } /* 注释补充:创建一个线程 Java 提供了三种创建线程的方法: 1.通过继承 Thread 类本身; 2.通过实现 Runnable 接口; 3.通过 Callable 和 Future 创建线程。 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。 */ package ajiayou.index0711.demo03; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Test2 { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService es = Executors.newFixedThreadPool(3); //创建了3个线程 Match2 m = new Match2(); m.setName("java"); Match2 m1 = new Match2(); m1.setName("mysql"); Match2 m2 = new Match2(); m2.setName("boot"); //开始对上面的线程进行执行 Future<Integer> future = es.submit(m); Future<Integer> future1 = es.submit(m1); Future<Integer> future2 = es.submit(m2); Integer i = future.get(); Integer i1 = future1.get(); Integer i2 = future2.get(); System.out.println(m.getName() + "共跑了" + i + "米"); System.out.println(m1.getName() + "共跑了" + i1 + "米"); System.out.println(m2.getName() + "共跑了" + i2 + "米"); } }
最新发布
07-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值