多线程Future

Future模式是多线程设计常用的一种设计模式。Future模式可以理解成:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。
Future提供了三种功能:
判断任务是否完成
能够中断任务
能够获取任务执行的结果
向线程池中提交任务的submit方法不是阻塞方法,而Future.get方法是一个阻塞方法,当submit提交多个任务时,只有所有任务都完成后,才能使用get按照任务的提交顺序得到返回结果,所以一般需要使用future.isDone先判断任务是否全部执行完成,完成后再使用future.get得到结果。(也可以用get (long timeout, TimeUnit unit)方法可以设置超时时间,防止无限时间的等待)
三段式的编程:1.启动多线程任务2.处理其他事3.收集多线程任务结果,Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作;要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
解决方法:CompletionService和CompletableFuture(按照任务完成的先后顺序获取任务的结果)

2、CompletionService是java1.8之前最好用的方法,

能够实现按照任务完成的先后顺序获取任务的结果。

public class TestCompletionService {
    private static final String commandstr01 = "hahah";
    private static final String commandstr02 = "hahah";
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //1、创建一个线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        
        CompletionService<String> completionService = new ExecutorCompletionService<String>(executorService);
        
        completionService.submit(new MyThreadt33(commandstr01));
        completionService.submit(new MyThreadt44(commandstr01));
            
        executorService.shutdown();
        
        System.out.println(completionService.take().get());
        System.out.println(completionService.take().get());
    }
}
 
class MyThreadt33 implements Callable<String>{
    private String commandstr;          // 要运行的mingling
    public MyThreadt33(String commandstr) {
        this.commandstr = commandstr;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            Thread.sleep(200);
            sum += i;
            System.out.println("Mythread3: "+i);
        }
        return String.valueOf(sum+300000);
    }
}
 
class MyThreadt44 implements Callable<String>{
    private String commandstr;          // 要运行的mingling
    public MyThreadt44(String commandstr) {
        this.commandstr = commandstr;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 50; i++) {
            Thread.sleep(200);
            sum += i;
            System.out.println("Mythread4: "+i);
        }
        return String.valueOf(sum+400000);
    }
}
CompletionService方法可以通过completionService.take().get()方法获取最快完成的线程的返回结果(若当前没有线程执行完成则阻塞直到最快的线程执行结束),第二次调用则返回第二快完成的线程的返回结果。

3、CompletableFuture接口

所谓异步调用其实就是实现一个可无需等待被调函数的返回值而让操作继续运行的方法。简单的讲就是另启一个线程来完成调用中的部分计算,使调用继续运行或返回,而不需要等待计算结果。但调用者仍需要取线程的计算结果。

JDK1.5新增了Future接口,用于描述一个异步计算的结果。虽然 Future 以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。

JDK1.8后提出了CompletableFuture接口实现了Future和CompletionStage两个接口,CompletionStage可以看做是一个异步任务执行过程的抽象(CompletionStage代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,一个阶段的计算执行可以是一个Function,Consumer或者Runnable。比如:

stage.thenApply(x -> square(x)).thenAccept(x -> System.out.print(x)).thenRun(() -> System.out.println()))
我们可以基于CompletableFuture创建任务和链式处理多个任务,并实现按照任务完成的先后顺序获取任务的结果。

(1)创建任务

##使用runAsync方法新建一个线程来运行Runnable对象(无返回值);

##使用supplyAysnc方法新建线程来运行Supplier<T>对象(有返回值);

##基于线程池创建

(2)任务的异步处理

不论Future.get()方法还是CompletableFuture.get()方法都是阻塞的,为了获取任务的结果同时不阻塞当前线程的执行,我们可以使用CompletionStage提供的方法结合callback来实现任务的异步处理。

##whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
##whenCompleteAsync:把 whenCompleteAsync 这个任务继续提交给线程池来进行执行,也就是并行执行。

##thenApply:当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化

##thenAccept:thenAccept接收上一阶段的输出作为本阶段的输入,并消费处理,无返回结果。 

##thenRun:不关心前一阶段的计算结果,因为它不需要输入参数,进行消费处理,无返回结果。

## thenCombine:会把两个 CompletionStage 的任务都执行完成后,把两个任务的结果一块交给 thenCombine 来处理。

## applyToEither :两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的转化操作。

##acceptEither 方法:两个CompletionStage,谁执行返回的结果快,我就用那个CompletionStage的结果进行下一步的消耗操作

public class TestCompletableFuture {
    private static final String commandstr01 = "hahah";
    private static final String commandstr02 = "hahah";
    private static final String commandstr03 = "hahah";
    private static final String commandstr04 = "hahah";
 
        public static void main(String[] args) throws InterruptedException, ExecutionException{
            
            ExecutorService executorService = Executors.newCachedThreadPool();
            
            CompletableFuture.supplyAsync(new MyThreadt444(commandstr02),executorService).whenComplete((result, e) -> {
                //执行线程执行完以后的操作。
                System.out.println(result + " " + e);
            }).exceptionally((e) -> {
                //抛出异常
                System.out.println("exception " + e);
                return "exception";
            });
            
             CompletableFuture.supplyAsync(new MyThreadt333(commandstr02),executorService).whenComplete((result, e) -> {
                //执行线程执行完以后的操作。
                System.out.println(result + " " + e);
            }).exceptionally((e) -> {
                System.out.println("exception " + e);
                return "exception";
            });
        }
}
 
 
 
class MyThreadt333 implements Supplier<String>{
 
    private String commandstr;          // 要运行的mingling
    public MyThreadt333(String commandstr) {
        this.commandstr = commandstr;
    }
    @Override
    public String get() {
        int sum = 0;
        for (int i = 0; i < 30; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            sum += i;
            System.out.println("Mythread333: "+i);
        }
        return String.valueOf(sum+300000);
    }
}
 
class MyThreadt444 implements Supplier<String>{
 
    private String commandstr;          // 要运行的mingling
    public MyThreadt444(String commandstr) {
        this.commandstr = commandstr;
    }
    @Override
    public String get() {
        int sum = 0;
        for (int i = 0; i < 40; i++) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            sum += i;
            System.out.println("Mythread444: "+i);
        }
        return String.valueOf(sum+400000);
    }
}
在CompletableFuture接口中除了使用whenComplete还可以使用handle等方法能实现按照任务完成的先后顺序获取任务的结果。

### Java 多线程 Future 使用教程及示例 #### 什么是 Future 接口? `Future` 是 `java.util.concurrent` 包中的一个接口,用于表示异步计算的结果。通过该接口可以检查任务是否完成、尝试取消任务以及获取任务的执行结果[^1]。 #### 主要方法 以下是 `Future` 接口中常用的方法: - **boolean isDone()**: 判断任务是否已经完成。 - **V get() throws InterruptedException, ExecutionException**: 阻塞当前线程直到任务完成并返回结果。如果任务抛出了异常,则会封装成 `ExecutionException` 抛出。 - **V get(long timeout, TimeUnit unit)**: 带超时时间的阻塞调用版本[^2]。 - **boolean cancel(boolean mayInterruptIfRunning)**: 尝试取消尚未完成的任务。参数指定是否允许中断正在运行的任务。 - **boolean isCancelled()**: 如果任务被成功取消则返回 true。 #### 实现方式 通常情况下,可以通过提交 Callable 对象到 ExecutorService 来获得 Future 实例。下面是一个简单的例子: ```java import java.util.concurrent.*; public class FutureExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); // 提交一个Callable任务给线程池 Future<Integer> future = executor.submit(() -> { Thread.sleep(2000); return 42; }); System.out.println("Task submitted..."); try { Integer result = future.get(3000, TimeUnit.MILLISECONDS); // 设置最大等待时间为3秒 System.out.println("Result from task: " + result); } catch (TimeoutException e) { System.err.println("The task timed out."); } executor.shutdown(); } } ``` 在这个程序里,我们创建了一个单线程的线程池,并向其中提交了一个耗时两秒钟的任务。主线程随后调用了 `future.get()` 方法来等待这个任务结束并取得其结果。如果超过设定的时间未得到响应将会触发 TimeoutException[^3]。 #### 关于 FutureTask 类型 除了直接使用 Future 接口外,还可以利用其实现类之一 —— `FutureTask` 。这是一个可包装 `Callable<V>` 或 `Runnable` 和相应结果对象的具体实现形式。它既实现了 Runnable 又实现了 Future ,因此可以直接传递给任何接受标准 Runnables 的组件而无需额外适配器[^4]。 这里给出一段基于 FutureTask 的代码片段作为补充说明: ```java import java.util.concurrent.Callable; class MyCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000L); return "Hello Concurrent World!"; } } public class FutureTaskDemo { public static void main(String[] args) throws Exception{ FutureTask<String> ft = new FutureTask<>(new MyCallable()); Thread t = new Thread(ft); t.start(); while(!ft.isDone()){ System.out.println("Waiting..."); Thread.sleep(500); } String s = ft.get(); System.out.println(s); } } ``` 上述实例展示了如何手动控制线程生命周期的同时还能享受到 Futures 所带来的便利之处。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值