Callable接口创建:
1,实现Callable接口,需要返回值类型
2,重写call方法,需要抛出异常
3,创建目标对象
4,创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(3);
5,执行提交:Future<Boolean> f1 = ser.submit(t1);
6,获取结果:boolean r1 = f1.get();
7,关闭服务:ser.shutdownNow();
案例:
package com.Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//实现Callable接口创建多线程
public class CallableTest implements Callable {
private String name; //保存的文件名
private String url; //网图地址
public CallableTest(String url, String name) {
this.url = url;
this.name = name;
}
//重写run方法,下载图片的执行体
@Override
public Object call() throws Exception {
WebDownLoader2 webDownLoader = new WebDownLoader2();
webDownLoader.downLoader(url, name);
System.out.println("下载了文件名为:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest t1 = new CallableTest("https://images.cnblogs.com/cnblogs_com/blogs/697817/galleries/2011482/o_2108131222251628857175898.png", "1.png");
CallableTest t2 = new CallableTest("https://images.cnblogs.com/cnblogs_com/blogs/697817/galleries/2011482/o_2108131222491628857044088.png", "2.png");
CallableTest t3 = new CallableTest("https://images.cnblogs.com/cnblogs_com/blogs/697817/galleries/2011482/o_2108131222251628857175898.png", "3.png");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> f1 = ser.submit(t1);
Future<Boolean> f2 = ser.submit(t2);
Future<Boolean> f3 = ser.submit(t3);
//获取结果
boolean r1 = f1.get();
boolean r2 = f2.get();
boolean r3 = f3.get();
//关闭服务
ser.shutdownNow();
}
}
//下载器
class WebDownLoader2{
//下载方法
public void downLoader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现问题");
}
}
}
/*
结果:
下载了文件名为:2.png
下载了文件名为:1.png
下载了文件名为:3.png
*/
Callable的好处:
1,可以定义返回值
2,可以抛出异常
三种方式的区别
1,采用Thread方式实现的线程不能继承其他父类,采用Runnable和Callable接口的可以继承其他父类,但是编程上采用Thread的方式可以直接使用getName()
方法,而采用Runnable和Callable接口的方式需要先获取Thread.currentThread();
2,采用Runnable和Callable的方式,可以多个线程公用一个Target对象,而采用Thread的方式不能,所以非常适合多个相同线程来处理同一份资源的情况
3,如果需要线程有返回值的需要使用Callable的方式。