futuretask异步线程全面入门

futuretask

为什么使用futuretask?

  1. 简化代码FutureTaskRunnableFuture 的实现,它自身就是一个 Runnable,可以直接提交给 Thread 对象执行,同时又包含了 Future 的所有特性。这种封装简化了代码,使您不需要手动管理线程的创建和结果的获取。
  2. 统一的错误处理: 使用 FutureTask 时,任何由 Callable 抛出的异常都会被封装在 ExecutionException 中,可以在调用 FutureTask.get() 时统一处理。如果直接使用 ThreadCallable,您需要自己管理线程执行和结果的收集,并且处理线程中的异常。
  3. 更好的取消和控制机制FutureTask 提供了方便的取消机制,通过调用 cancel() 方法可以取消任务的执行。而使用 ThreadCallable,您需要自行实现任务取消的逻辑。
  4. 结果存储FutureTask 在内部处理了结果的存储和检索。使用 ThreadCallable,您需要自己设法存储和访问 Callable 返回的结果。
  5. 阻塞和等待机制FutureTask.get() 方法提供了等待任务完成的能力,而且可以选择设置超时。这在使用 ThreadCallable 组合时不那么直接。
  6. 与并发工具的兼容性FutureTask 可以很容易地与 Executor 框架(如 ExecutorService)一起使用,提供更灵活的线程池管理。虽然您也可以将 Callable 提交给 ExecutorService,但 FutureTask 提供了更多控制和查询执行状态的能力。

总之,虽然在某些情况下使用 ThreadCallable 可以达到相似的效果,但 FutureTask 提供了一个更简洁、更强大的解决方案,尤其是在需要任务的取消、结果的存储和检索、异常处理以及与线程池协作等方面

Java中的FutureTask是一个非常有用的并发工具,它实现了Future接口和Runnable接口。以下是FutureTask的一些优缺点:

优点

  1. 异步计算FutureTask可以异步执行一个计算任务,并且可以在未来的某个时刻获取其结果。这对于改善程序的响应性和性能非常有帮助。
  2. 结果重用:一旦FutureTask完成,就可以多次且方便地获取其结果,而无需重新计算。
  3. 异常处理:如果在任务执行过程中发生异常,该异常会被捕获并在调用get()时重新抛出。
  4. 取消操作:可以通过cancel()方法取消任务的执行。如果任务已经开始,根据cancel方法的参数,可以选择是否中断正在执行的任务。
  5. 超时机制:通过get(long timeout, TimeUnit unit)方法,可以设置获取结果的超时时间。

缺点

  1. 复杂性:使用FutureTask比简单的线程执行复杂,需要正确管理任务的提交、执行、取消和结果处理。
  2. 资源消耗:与直接执行任务相比,FutureTask需要更多的系统资源,因为它涉及到线程管理和上下文切换。
  3. 阻塞调用get()方法是阻塞的,会导致调用线程等待直到任务完成。这可能导致性能问题,尤其是在任务执行时间很长的情况下。
  4. 限制性FutureTask提供的控制机制相对基础,对于更复杂的并发场景,可能需要更高级的工具,如CompletableFuture
  5. 取消的限制:一旦任务开始执行,取消任务可能并不总是可行的,尤其是当任务不响应中断时。

基本使用

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class class1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> f = new FutureTask<>(new MyThread2());
        Thread t1 = new Thread(f);
        t1.setDaemon(true);
        t1.start();
        System.out.println(f.get());  // 调用get方法后会一直阻塞
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "主线程");
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {

    }
}

class MyThread2 implements Callable<String>{

    @Override
    public String call() throws Exception {
        return "dd";
    }
}

get方法只等待3秒

import java.util.concurrent.*;

public class class1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask<String> f = new FutureTask<>(new MyThread2());
        Thread t1 = new Thread(f);
        t1.setDaemon(true);
        t1.start();
        System.out.println(f.get(3,TimeUnit.SECONDS));
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "主线程");
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {

    }
}

class MyThread2 implements Callable<String>{

    @Override
    public String call() throws Exception {
        TimeUnit.SECONDS.sleep(5000);
        return "dd";
    }
}

会抛出一下异常

image-20240125194002379

isDone轮询方式

import java.util.concurrent.*;

public class class1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask<String> f = new FutureTask<>(new MyThread2());
        Thread t1 = new Thread(f);
        t1.setDaemon(true);
        t1.start();

        // -------------------- 轮询方式进行
        while(true){
            if(f.isDone()){
                System.out.println(f.get());
                break;
            }else{
                TimeUnit.MILLISECONDS.sleep(500);
                System.out.println("等一会");
            }
        }
        // ------------------------------


        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "主线程");
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {

    }
}

class MyThread2 implements Callable<String>{

    @Override
    public String call() throws Exception {
        TimeUnit.SECONDS.sleep(5);
        return "dd";
    }
}

优点
  1. 非阻塞:轮询提供了一种非阻塞方式来检查任务的完成状态,这意味着你可以在等待任务完成的同时执行其他操作。
  2. 灵活性:你可以控制轮询的频率和逻辑,根据具体需求调整轮询机制。
缺点
  1. 资源消耗:频繁的轮询可能会消耗系统资源,特别是当轮询间隔很短时。
  2. 响应时间:轮询机制可能会引入延迟,因为任务可能在轮询检查之间已经完成。
  3. 复杂性增加:相比直接调用get()阻塞等待结果,轮询增加了代码的复杂性。
  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值