一、为啥要引入Callable
在前面讲了通过继承Thread和实现Runnable方式创建线程的区别,那为什么有了Runnable还要引入Callable?下面通过实现Runnable方式的弊端给出答案
实现Runnable方式的弊端:
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
package java.util.concurrent;
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
通过上面两个接口的定义可以看出
1、实现Runnable的方式没有返回值
2、实现Runnable的方式无法抛出异常
注:实现Callable方式是JDK1.5引入的
二、如何通过实现Callable方式创建线程
如何创建
这种方式创建线程需要配合FutureTask或者线程池来实现,下面给出通过配合FutureTask来创建线程的代码
public static void main(String[] args) {
//1、定义一个Callable任务
Callable<String> task = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("任务正在执行"+Thread.currentThread().getName());
return "OK";
}
};
//2、创建FutureTask对象,传入刚创建的task
FutureTask<String> futureTask = new FutureTask<>(task);
//3、创建线程并执行FutureTask
Thread thread = new Thread(futureTask);
thread.start();
}
运行结果:
如何获取返回值
通过FutureTask的get方法可以获取返回值
public static void main(String[] args) {
//1、定义一个Callable任务
Callable<String> task = new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("任务正在执行"+Thread.currentThread().getName());
return "OK";//返回一个值
}
};
//2、创建FutureTask对象,传入刚创建的task
FutureTask<String> futureTask = new FutureTask<>(task);
//3、创建线程并执行FutureTask
Thread thread = new Thread(futureTask);
thread.start();
//4、获取返回值
try {
String result = futureTask.get();
System.out.println("返回值:"+result);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
执行结果:
因为call方法可能会抛出异常所以需要使用try…catch捕获
扫描二维码关注公众号,回复:
17439702 查看本文章
取消当前线程
FutureTask.java
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally {
// final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
FutureTask类中提供了一个cancel方法用于取消当前线程,下面是使用实例
public static void main(String[] args) {
//1、定义一个Callable任务
Callable<String> task = new Callable<String>() {
@Override
public String call() throws Exception {
try {
for (int i = 0; i < 10; i++) {
System.out.println("Task running... " + i);
Thread.sleep(1000); // 模拟任务的执行
}
return "OK";
} catch (InterruptedException e) {
System.out.println("Task was interrupted");
throw e;
}
}
};
//2、创建FutureTask对象,传入刚创建的task
FutureTask<String> futureTask = new FutureTask<>(task);
//3、创建线程并执行FutureTask
Thread thread = new Thread(futureTask);
thread.start();
try {
// 主线程等待 3 秒钟,然后取消任务
Thread.sleep(3000);
System.out.println("Cancelling the task...");
futureTask.cancel(true); // 取消任务,并设置为允许中断线程
// 检查任务是否被取消
if (futureTask.isCancelled()) {
System.out.println("Task was cancelled");
} else {
// 尝试获取任务结果(这行代码可能永远不会执行,因为任务已经被取消)
String result = futureTask.get();
System.out.println("Task result: " + result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
执行结果:
注:实现Runnable创建线程的方式是没有cancel方法来取消当前任务的
三、配合线程池一起使用
public static void main(String[] args) {
// 创建一个固定线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 定义一个 Callable 任务
Callable<String> task = new Callable<String>() {
@Override
public String call() throws Exception {
if (Math.random() > 0.5) {
throw new RuntimeException("Random failure occurred");
}
return "Task completed successfully";
}
};
// 提交任务给线程池并获取 Future 对象
Future<String> future = executorService.submit(task);
try {
// 获取任务的结果
String result = future.get();
System.out.println("Result: " + result);
} catch (InterruptedException e) {
// 线程被中断时的处理
e.printStackTrace();
} catch (ExecutionException e) {
// call 方法抛出的异常会被封装在 ExecutionException 中
Throwable cause = e.getCause();
if (cause instanceof Exception) {
// 处理 call 方法抛出的异常
System.err.println("Exception caught: " + cause.getMessage());
}
}
// 关闭线程池
executorService.shutdown();
}
执行结果:
当call方法中Math.random() > 0.5成立时执行结果为
四、总结
1、实现Callable的方式有返回值
2、实现Callable的方式可以抛出异常
3、实现Callable的方式可以通过FutureTask类中提供了一个cancel方法取消任务