重学多线程之一:Future

一:什么是Future ?

Future 接口是 Java 多线程编程中的一个核心概念,用来表示异步计算任务的结果。它定义了一组方法,可以用来查询任务是否已经完成、获取任务的返回值或者等待任务完成后获取其返回值
当我们需要从网络上下载一个大文件时,因为网络速度的限制,下载操作需要花费一些时间来完成,如果在主线程中同步执行下载操作,会导致界面卡顿,用户体验不好。这时就可以使用 Future 接口来异步执行下载操作,将下载任务交给另一个线程去执行,主线程可以继续执行其他操作,等待下载任务完成后再获取下载结果。

import java.util.concurrent.*;
public class FutureExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newSingleThreadExecutor(); 
        // 执行一个需要时间较长的下载任务,并返回一个 Future 对象
        Future<String> future = executorService.submit(() -> {
            System.out.println("Downloading...");
            Thread.sleep(5000); // 模拟下载操作需要 5s
            return "Downloaded file";
        });
        
        // 主线程可以继续执行其他操作,异步任务在另一个线程中执行
        System.out.println("Do something else...");
        
        // 等待异步任务完成,并获取任务的返回结果
        System.out.println("Wait result...");
        String result = future.get();
        System.out.println("Result: " + result);
        
        executorService.shutdown(); // 关闭线程池
    }
}

二:什么是FutureTask?

FutureTask是Future 的常用子类

此时当我们去通过new thread 创建一个线程,thread 类如下

public  class Thread implements Runnable

只能通过传Runnable的方式创建

此时,juc下提供了RunnableFuture接口,同时继承了Runnable和Future如下

public interface RunnableFuture<V> extends Runnable, Future<V>

此时可以通过new Thread(FutureTask)的方式创建线程

三:创建线程

每次都通过手动创建比较繁琐,时伴随垃圾回收,开销大,可以通过线程池的方式创建线程

executorService=Executors.newFixedThreadPool(3);
这段代码就是线程池中已经创建三个线程

四: Future弊端

以上案例中有一个get方法
在获取Future中的结果时,可以通过调用Future对象的get()方法来阻塞等待异步任务的结果返回。如果异步任务还没有完成,则get()方法会一直阻塞直到任务完成并返回结果。如果任务执行过程中出现异常,则get()方法会抛出异常。此方法有以下弊端

01 阻塞主线程: Future.get()方法是阻塞的,意味着在调用该方法时,主线程将等待异步任务完成。这可能会导致主线程被阻塞,并影响应用程序的性能和响应性

02 无法取消任务: Future.get()方法是无法取消的,这意味着如果您的异步任务正在执行中,您只能等待它完成

五:CompletableFuture对Future的改进

CompletableFuture 实现非阻塞的方式是使用回调函数。当异步任务完成时,CompletableFuture 会自动调用注册的回调函数,而不是阻塞当前线程等待结果。这样可以避免阻塞线程,提高程序的并发性能。
下面是一个使用 CompletableFuture 实现异步任务的例子

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    // 异步任务
    return "Hello, world!";
});

// 注册回调函数
future.thenAccept(result -> {
    System.out.println("异步任务完成,结果为:" + result);
});

// 主线程继续执行其他任务
System.out.println("主线程继续执行其他任务...");

六:如何创建CompletableFuture

1 runAsync 无返回值(传runnable接口)
不指定线程池,使用默认的,,指定线程池就用我们自己的

2 supplyAsync 有返回值(Supplier)

你可能感兴趣的:(java,jvm,开发语言)