一、为啥要引入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>() {@Overridepublic String call() throws Exception {System.out.println("任务正在执行"+Thread.currentThread().getName());return "OK";}};//2、创建FutureTask对象,传入刚创建的taskFutureTask<String> futureTask = new FutureTask<>(task);//3、创建线程并执行FutureTaskThread thread = new Thread(futureTask);thread.start();}
运行结果:
如何获取返回值
通过FutureTask的get方法可以获取返回值
public static void main(String[] args) {//1、定义一个Callable任务Callable<String> task = new Callable<String>() {@Overridepublic String call() throws Exception {System.out.println("任务正在执行"+Thread.currentThread().getName());return "OK";//返回一个值}};//2、创建FutureTask对象,传入刚创建的taskFutureTask<String> futureTask = new FutureTask<>(task);//3、创建线程并执行FutureTaskThread 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捕获
取消当前线程
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 exceptionif (mayInterruptIfRunning) {try {Thread t = runner;if (t != null)t.interrupt();} finally { // final stateUNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);}}} finally {finishCompletion();}return true;
}
FutureTask类中提供了一个cancel方法用于取消当前线程,下面是使用实例
public static void main(String[] args) {//1、定义一个Callable任务Callable<String> task = new Callable<String>() {@Overridepublic 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对象,传入刚创建的taskFutureTask<String> futureTask = new FutureTask<>(task);//3、创建线程并执行FutureTaskThread 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>() {@Overridepublic 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方法取消任务