JAVA多线程—Callable详解
·
目录
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
Executors:一个线程池的工厂类,通过此类的静态工厂方法可以创建多种类型的线程池对象。
1.两种接口的区别
-
与使用Runnable接口相比, Callable功能更强大些
-
相比run()方法,可以有返回值
-
方法可以抛出异常
-
支持泛型的返回值(需要借助FutureTask类,获取返回结果)
-
- Callable规定的方法是call(),Runnable规定的方法是run().
//Callable 接口 public interface Callable<V> { V call() throws Exception; } // Runnable 接口 public interface Runnable { public abstract void run(); }
- 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可cancel()取消任务的执行,还可get()获取执行结果。
- 缺点:在获取分线程执行结果的时候,当前线程(或是主线程)受阻塞,效率较低。
2.Callable两种执行方式
2.1借助FutureTask执行
2.1.1什么是Future
它有以下五种方法:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
//isCancelled:如果此任务在正常完成之前被取消,则返回true。
boolean isCancelled();
//isDone:如果此任务完成,则返回true。
boolean isDone();
//get:等待任务完成,然后返回其结果。是一个阻塞方法
V get() throws InterruptedException, ExecutionException;
//get(long timeout, TimeUnit unit):等待任务完成,然后返回其结果。
//如果在指定时间内,还没获取到结果,就直接返回null。
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
/*cancel:用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
*参数mayInterruptIfRunning表示是否取消正在执行却没有执行完毕的任务,如果设置true,
*则表示可以取消正在执行过程中的任务。
*如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,
*若mayInterruptIfRunning设置为false,不会取消恩物,返回false;如果任务还没有执行则无论*mayInterruptIfRunning为true还是false,肯定返回true。
*如果任务已经完成则无论mayInterruptIfRunning为true还是false,一定会返回false;*/
- FutureTask类实现了RunnableFuture接口,而RunnnableFuture接口继承了Runnable和Future接口,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
- FutureTask可以用来包装Callable或者Runnbale对象。因为FutureTask实现了Runnable接口,所以FutureTask也可以被提交给Executor(如上面例子那样)。
例如:创建Callable线程计算和并获取
//1.创建一个实现Callable的实现类
class MyCallable implements Callable {
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
System.out.println("子线程在进行计算");
int sum = 0;
for (int i = 0;i < 5; i++) {
sum = sum + i;
}
return sum;
}
}
public class TestMyCallable {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
MyCallable mc = new MyCallable();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask ft = new FutureTask<>(mc);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(ft).start();
System.out.println("主线程在执行任务");
//6.get()接收返回值
try {
Object sum = ft.get();
System.out.println("和为:"+sum);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("所有任务执行完毕");
}
}
执行结果:
2.2借助线程池来运行执行
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,即执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
-
ExecutorService
:真正的线程池接口。常见子类ThreadPoolExecutorvoid execute(Runnable command)
执行任务/命令,没有返回值,一般用来执行Runnable <T> Future<T> submit(Callable<T> task)
执行任务,有返回值,一般又来执行Callable void shutdown()
关闭连接池 -
Executors
:一个线程池的工厂类,通过此类的静态工厂方法可以创建多种类型的线程池对象。Executors.newCachedThreadPool()
创建一个可根据需要创建新线程的线程池 Executors.newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池 Executors.newSingleThreadExecutor()
创建一个只有一个线程的线程池 Executors.newScheduledThreadPool(int corePoolSize)
创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
3.示例和结果:
class MyCallable implements Callable {
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() {
System.out.println("Callable子线程在进行计算偶数和");
int evenSum = 0;
for(int i = 0;i <= 100;i++){
if(i % 2 == 0){
evenSum += i;
}
}
return evenSum;
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("Runnable子线程在进行计算奇数和");
int oddSum = 0;
for(int i = 0;i <= 100;i++){
if(i % 2 != 0){
oddSum += i;
}
}
System.out.println("奇数和为:"+oddSum);
System.out.println("Runnable子线程执行完毕");
}
}
public class TestMyCallable2 {
public static void main(String[] args) {
System.out.println("主线程在执行任务");
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new MyRunnable());//适合适用于Runnable
try {
Future<?> future = service.submit(new MyCallable());//适合使用于Callable
System.out.println("偶数和为:" + future.get());
System.out.println("Callable子线程执行完毕");
} catch (Exception e) {
e.printStackTrace();
}
//3.关闭连接池
service.shutdown();
System.out.println("所有任务执行完毕");
}
}
运行结果:
更多推荐
已为社区贡献1条内容
所有评论(0)