Future和Callable的使用总结

在默认的情况下,线程Thread对象不具有返回值的功能,如果在需要取得返回值的情况下是极为不方便的,但在Java1.5的并发包中可以使用Future和Callable来使线程具有返回值的功能。

1.Future和Callable的介绍

接口Callable与线程功能密不可分,但和Runnable的主要区别为:

(1) 接口Callable的call()方法可以有返回值,但Runnable接口的run()方法没有返回值。

(2) Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。

执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。

2.方法get()结合ExecutorService中的submit(Callable<T>)的使用

方法submit(Callable<T>)可以执行参数为Callable的任务,方法get()用于获得返回值,示例如下:

package mycallable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

	private int age;

	public MyCallable(int age) {
		super();
		this.age = age;
	}

	public String call() throws Exception {
		Thread.sleep(8000);
		return "返回值 年龄是:" + age;
	}

}
package test.run;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import mycallable.MyCallable;

public class Run {

	public static void main(String[] args) throws InterruptedException {
		try {
			MyCallable callable = new MyCallable(100);

			ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L,
					TimeUnit.SECONDS, new LinkedBlockingDeque());
			Future<String> future = executor.submit(callable);
			System.out.println("main A " + System.currentTimeMillis());
			System.out.println(future.get());
			System.out.println("main B " + System.currentTimeMillis());
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

3.方法get()结合ExecutorService中的submit(Runnable)和isDone()的使用

方法submit()不仅可以传入Callable对象,也可以传入Runnable对象,说明submit方法支持有返回值和无返回值的功能。如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性。

package test.run;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 如果submit方法传入Callable接口则可以有返回值,如果传入Runnable则无返回值,打印的结果就是null。方法get()具有阻塞特性,而isDone()方法无阻塞特性
 * @author linhaiy
 * @date 2019.03.06
 */
public class Run {

	public static void main(String[] args) {
		try {
			Runnable runnable = new Runnable() {
				@Override
				public void run() {
					System.out.println("打印的信息");
				}
			};
			ExecutorService executorRef = Executors.newCachedThreadPool();
			Future future = executorRef.submit(runnable);
			System.out.println(future.get() + " " + future.isDone());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

4.使用ExecutorService接口中的方法submit(Runnable,T result)

方法submit(Runnable,T result)的第2个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得。

package entity;

public class Userinfo {

	private String username;
	private String password;

	public Userinfo() {
		super();
	}

	public Userinfo(String username, String password) {
		super();
		this.username = username;
		this.password = password;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}
package myrunnable;

import entity.Userinfo;

public class MyRunnable implements Runnable {

	private Userinfo userinfo;

	public MyRunnable(Userinfo userinfo) {
		super();
		this.userinfo = userinfo;
	}

	@Override
	public void run() {
		userinfo.setUsername("usernameValue");
		userinfo.setPassword("passwordValue");
	}
}
package test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import myrunnable.MyRunnable;
import entity.Userinfo;

public class Test {

	FutureTask abc;   //接口Future的实现类

	public static void main(String[] args) {
		try {
			Userinfo userinfo = new Userinfo();
			MyRunnable myrunnable = new MyRunnable(userinfo);

			ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 10,
					TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
			Future<Userinfo> future = pool.submit(myrunnable, userinfo);
			System.out.println(future);
			System.out.println("begin time=" + System.currentTimeMillis());
			userinfo = future.get();
			System.out.println("get value " + userinfo.getUsername() + " "
					+ userinfo.getPassword());
			System.out.println("  end time=" + System.currentTimeMillis());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

5.方法cancel(boolean mayInterruptIfRunning) 和isCancelled()的使用

方法cancel(boolean mayInterruptIfRunning)的参数mayInterruptIfRunning的作用是:如果线程正在运行则是否中断正在运行的线程,在代码中需要使用if(Thread.currentThread().isInterrupted())进行配合。

方法cancel()的返回值代表发送取消任务的命令是否成功完成。

6.方法get(long timeout,TimeUnit unit)的使用

方法get(long timeout,TimeUnit unit)的作用是在指定的最大时间内等待获得返回值。

package mycallable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {
	public String call() throws Exception {     
		Thread.sleep(10000);                    //目的是为了在指定时间内获取不到返回值
		System.out.println("sleep 10秒执行完了!");
		return "anyString";
	}
}
package test.run;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import mycallable.MyCallable;

public class Run {

	public static void main(String[] args) {
		try {
			MyCallable callable = new MyCallable();
			ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L,
					TimeUnit.SECONDS, new LinkedBlockingDeque());
			System.out.println("begin " + System.currentTimeMillis());
			Future<String> future = executor.submit(callable);
			System.out.println("返回值" + future.get(5, TimeUnit.SECONDS));
			System.out.println("  end " + System.currentTimeMillis());
		} catch (InterruptedException e) {
			System.out.println("进入catch InterruptedException");
			e.printStackTrace();
		} catch (ExecutionException e) {
			System.out.println("进入catch ExecutionException");
			e.printStackTrace();
		} catch (TimeoutException e) {
			System.out.println("进入catch TimeoutException");
			e.printStackTrace();
		}
	}
}

7.自定义拒绝策略RejectedExecutionHandler接口的使用

接口RejectedExecutionHandler的主要作用是当线程池关闭后依然有任务要执行时,可以实现一些处理。

package com.executionhandler;

import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
	public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
		System.out.println(((FutureTask) r).toString() + " 被拒绝!");
	}
}
package com.executionhandler;

public class MyRunnable implements Runnable {

	private String username;

	public MyRunnable(String username) {
		super();
		this.username = username;
	}

	public void run() {
		System.out.println(username + " 在运行!");
	}
}
package com.executionhandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class Run {

	public static void main(String[] args) {

		ExecutorService service = Executors.newCachedThreadPool();
		ThreadPoolExecutor executor = (ThreadPoolExecutor) service;
		executor.setRejectedExecutionHandler(new MyRejectedExecutionHandler());
		service.submit(new MyRunnable("A"));
		service.submit(new MyRunnable("B"));
		service.submit(new MyRunnable("C"));
		executor.shutdown();
		service.submit(new MyRunnable("D"));

	}
}

8.方法execute()与submit()的区别

(1) 方法execute()没有返回值,而submit()方法可以有返回值。

(2) 方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义ThreadFactory的方式进行捕获,而submit()方法在默认的情况下,可以catch Execution-Exeception捕获异常

9.验证Future的缺点

package mycallable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

	private String username;
	private long sleepValue;

	public MyCallable(String username, long sleepValue) {
		super();
		this.username = username;
		this.sleepValue = sleepValue;
	}

	@Override
	public String call() throws Exception {
		System.out.println(username);
		Thread.sleep(sleepValue);
		return "return " + username;
	}
}
package test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import mycallable.MyCallable;

public class Test {

	public static void main(String[] args) {
		try {
			MyCallable callable1 = new MyCallable("username1", 5000);
			MyCallable callable2 = new MyCallable("username2", 4000);
			MyCallable callable3 = new MyCallable("username3", 3000);
			MyCallable callable4 = new MyCallable("username4", 2000);
			MyCallable callable5 = new MyCallable("username5", 1000);

			List<Callable> callableList = new ArrayList<Callable>();
			callableList.add(callable1);
			callableList.add(callable2);
			callableList.add(callable3);
			callableList.add(callable4);
			callableList.add(callable5);

			List<Future> futureList = new ArrayList<Future>();

			ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS,
					new LinkedBlockingDeque<Runnable>());
			for (int i = 0; i < 5; i++) {
				futureList.add(executor.submit(callableList.get(i)));
			}
			System.out.println("run first time=  " + System.currentTimeMillis());
			for (int i = 0; i < 5; i++) {
				System.out.println(futureList.get(i).get() + " " + System.currentTimeMillis());
			}
			// 按顺序打印的效果
			// 说明一个Future对应指定的一个Callable
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

总结:Future和Callable这两个接口的优点就是从线程中返回数据以便进行后期的处理,但是FutureTask类也有其自身的缺点,就是阻塞性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潇潇雨歇_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值