简述CompletableFuture异步任务编排

news2025/1/16 4:52:02

前言

在之前的项目开发中,都没怎么使用过CompletableFuture的功能,只听说过和异步编程有关。为了能够在将来有需要的时候用得上,这两天花了点时间学习了一下,并简单地总结一下如何使用CompletableFuture完成异步任务编排。

先创建一个自定义的线程池,后续所有代码都会使用到:

  private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(3, 5, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), new ThreadFactory() {
    private final AtomicInteger THREAD_NUM = new AtomicInteger(1);
​
    @Override
    public Thread newThread(Runnable r) {
      Thread t = new Thread(r);
//      设置为守护线程,main线程结束就跟着一起结束,否则main函数结束jvm还在
      t.setDaemon(true);
      t.setName("completable-future-test-Thread-" + THREAD_NUM.incrementAndGet());
      return t;
    }
  }, new ThreadPoolExecutor.AbortPolicy());
复制代码

同步串行

同步串行代表任务1、任务2、任务3按时间先后顺序执行,并且都是同一个线程来执行。

示例代码如下:

CompletableFuture
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
        .thenApply(
            (task1Result) -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println("拿到上一个任务的返回值:" + task1Result);
              System.out.println(taskName + "执行结束");
              return taskName;
            })
        .thenAccept(
             (task2Result) -> {
             Thread currentThread = Thread.currentThread();
             String ThreadName = currentThread.getName();
             String taskName = "task3";
             System.out.println(ThreadName + "开始执行任务:" + taskName);
             System.out.println("正在执行任务" + taskName);
             System.out.println("拿到上一个任务的返回值:" + task2Result);
             System.out.println(taskName + "执行结束");
           });
复制代码

执行结果:

completable-future-test-Thread-2开始执行任务:task1
正在执行任务task1
task1执行结束
completable-future-test-Thread-2开始执行任务:task2
正在执行任务task2
拿到上一个任务的返回值:task1
task2执行结束
completable-future-test-Thread-2开始执行任务:task3
正在执行任务task3
拿到上一个任务的返回值:task2
task3执行结束
复制代码

1.入口函数supplyAsync()代表一个异步的有返回值的函数,之所以异步,是与主线程区别,从线程池中的拿一个线程来执行。

2.thenApply()thenAccept()没有Async,意味着是和前面的任务共用一个线程,从执行结果上我们也可以看到线程名称相同。

3.thenApply()需要接收上一个任务的返回值,并且自己也要有返回值。

4.thenAccept()需要接收上一个任务的返回值,但是它不需要返回值。

异步串行

异步串行代表任务1、任务2、任务3按时间先后顺序执行,并由不同的线程来执行。

示例代码如下:

    CompletableFuture
        // 有返回值
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
        // 需要上一个任务的返回值,并且自身有返回值
        .thenApplyAsync(
            (task1Result) -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println("拿到上一个任务的返回值:" + task1Result);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
        // 不需要上一个任务的返回值,自身也没有返回值
        .thenRunAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println("thenRunAsync()不需要上一个任务的返回值");
              System.out.println(taskName + "执行结束");
            }, THREAD_POOL_EXECUTOR);
复制代码

执行结果如下:

completable-future-test-Thread-2开始执行任务:task1
正在执行任务task1
task1执行结束
completable-future-test-Thread-3开始执行任务:task2
正在执行任务task2
拿到上一个任务的返回值:task1
task2执行结束
completable-future-test-Thread-4开始执行任务:task3
正在执行任务task3
thenRunAsync()不需要上一个任务的返回值
task3执行结束
复制代码

1.入口函数依然是supplyAsync(),需要传入一个有返回值的函数作为参数;如果想要没有返回值的函数传进来的话,可以使用CompletableFuture.runAsync();

2.thenApplyAsync()thenRunAsync()分别表示里面的任务都是异步执行的,和执行前面的任务不是同一个线程;

3.thenRunAsync()需要传入一个既不需要参数,也没有返回值的任务;

并行任务

并行代表任务1、任务2、任务3没有依赖关系,分别由不同的线程执行;

示例代码如下:

    CompletableFuture<String> future1 = CompletableFuture
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture<Void> future2 = CompletableFuture
        .runAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture<String> future3 = CompletableFuture
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
复制代码

执行结果如下:

completable-future-test-Thread-4开始执行任务:task3
completable-future-test-Thread-2开始执行任务:task1
completable-future-test-Thread-3开始执行任务:task2
正在执行任务task3
task3执行结束
正在执行任务task2
正在执行任务task1
task2执行结束
task1执行结束
复制代码

一看执行结果,明显是乱序的,并且三个任务分别由三个线程执行,符合咱们的预期;注意异步的方法后面都是带有Async关键字的;

多任务结果合并计算

  • 两个任务结果的合并

任务3的执行依赖于任务1、任务2的返回值,并且任务1和任务3由同一个线程执行,任务2单独一个线程执行;

示例代码如下:

    CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
        .thenCombine(
            CompletableFuture
                // 任务2
                .supplyAsync(
                    () -> {
                      Thread currentThread = Thread.currentThread();
                      String ThreadName = currentThread.getName();
                      String taskName = "task2";
                      System.out.println(ThreadName + "开始执行任务:" + taskName);
                      System.out.println("正在执行任务" + taskName);
                      System.out.println(taskName + "执行结束");
                      return taskName;
                    }, THREAD_POOL_EXECUTOR),
            // 任务3
            (task1Result, task2Result) -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("task1结果:" + task1Result + "\ttask2结果:" + task2Result);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            });
复制代码

执行结果如下:

completable-future-test-Thread-3开始执行任务:task2
completable-future-test-Thread-2开始执行任务:task1
正在执行任务task1
正在执行任务task2
task2执行结束
task1执行结束
completable-future-test-Thread-2开始执行任务:task3
task1结果:task1 task2结果:task2
正在执行任务task3
task3执行结束
复制代码

CompletableFuture提供了thenCombine()来合并另一个CompletableFuture的执行结果,所以thenCombine()需要两个参数,第一个参数是另一个CompletableFuture,第二个参数会收集前两个任务的返回值,类似下面这样:

(result1,result2)->{
  // 执行业务逻辑
  return result3;
}
复制代码

如果小伙伴们想要实现任务3也是单独的线程执行的话,可以使用thenCombineAsync()这个方法。代码如下:

    CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
​
        .thenCombineAsync(
            CompletableFuture
                // 任务2
                .supplyAsync(
                    () -> {
                      Thread currentThread = Thread.currentThread();
                      String ThreadName = currentThread.getName();
                      String taskName = "task2";
                      System.out.println(ThreadName + "开始执行任务:" + taskName);
                      System.out.println("正在执行任务" + taskName);
                      System.out.println(taskName + "执行结束");
                      return 2;
                    }, THREAD_POOL_EXECUTOR),
            // 任务3
            (task1Result, task2Result) -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("task1结果:" + task1Result + "\ttask2结果:" + task2Result);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return 2L;
            }, THREAD_POOL_EXECUTOR);
复制代码

如果任务3中不需要返回结果,可以使用thenAcceptBoth()thenAcceptBothAsync(),使用方式与thenCombineAsync()类似;

  • 多任务结果合并

示例代码如下:

    CompletableFuture future1 = CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future2 = CompletableFuture
        // 任务2
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future3 = CompletableFuture
        // 任务3
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture[] futures = new CompletableFuture[]{future1, future2, future3};
    CompletableFuture.allOf(futures)
        // 任务4
        .whenCompleteAsync(
            (v, e) -> {
              List<Object> values = new ArrayList<>();
              for (CompletableFuture future : futures) {
                try {
                  values.add(future.get());
                } catch (Exception ex) {
                }
              }
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task4";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("前面任务的处理结果:" + values);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
            }, THREAD_POOL_EXECUTOR);
复制代码

执行结果如下:

completable-future-test-Thread-3开始执行任务:task2
completable-future-test-Thread-4开始执行任务:task3
completable-future-test-Thread-2开始执行任务:task1
正在执行任务task2
正在执行任务task3
正在执行任务task1
task2执行结束
task3执行结束
task1执行结束
completable-future-test-Thread-2开始执行任务:task4
前面任务的处理结果:[task1, task2, task3]
正在执行任务task4
task4执行结束
复制代码

之所以最后任务4的线程是completable-future-test-Thread-2,那是因为线程池的核心线程数设置为3,线程数设置高一点就会创建新的线程处理;

从上述代码示例中,我们可以收获到另一个知识点:allOf(),它的作用是要求所有的任务全部完成才能执行后面的任务。

任一任务完成

在一批任务中,只要有一个任务完成,那么就可以向后继续执行其他任务。

为了代码演示无异议,后续代码中,我们把线程数提升到4。

示例代码如下:

    CompletableFuture future1 = CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future2 = CompletableFuture
        // 任务2
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future3 = CompletableFuture
        // 任务3
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture.anyOf(future1, future2, future3)
        .thenApplyAsync((taskResult) -> {
          Thread currentThread = Thread.currentThread();
          String ThreadName = currentThread.getName();
          String taskName = "task4";
          System.out.println(ThreadName + "开始执行任务:" + taskName);
          System.out.println("前面任务的处理结果:" + taskResult);
          System.out.println("正在执行任务" + taskName);
          System.out.println(taskName + "执行结束");
          return taskName;
        }, THREAD_POOL_EXECUTOR);
复制代码

执行结果如下:

completable-future-test-Thread-2开始执行任务:task1
completable-future-test-Thread-4开始执行任务:task3
completable-future-test-Thread-3开始执行任务:task2
正在执行任务task3
正在执行任务task2
正在执行任务task1
task1执行结束
task3执行结束
task2执行结束
completable-future-test-Thread-5开始执行任务:task4
前面任务的处理结果:task1
正在执行任务task4
task4执行结束
复制代码

可以看到,任务1第一个结束,所以任务4中接收到的执行结果就是任务1的返回值。

快速失败

在一批任务当中,只要有任意一个任务执行产生异常了,那么就直接结束;否则就要等待所有任务成功执行完毕。

示例代码如下:

    CompletableFuture future1 = CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future2 = CompletableFuture
        // 任务2
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task2";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
​
              throw new RuntimeException("任务2异常!");
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture future3 = CompletableFuture
        // 任务3
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task3";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              throw new RuntimeException("任务3异常!");
            }, THREAD_POOL_EXECUTOR);
    CompletableFuture[] futures = new CompletableFuture[]{future1, future2, future3};
    CompletableFuture allCompletableFuture = CompletableFuture.allOf(futures);
    // 创建一个任务来监听异常
    CompletableFuture<?> anyException = new CompletableFuture<>();
    for (CompletableFuture<?> completableFuture : futures) {
      completableFuture.exceptionally((t) -> {
        // 任何一个任务异常都会让anyException任务完成
        anyException.completeExceptionally(t);
        return null;
      });
    }
    // 要么allCompletableFuture全部成功,要么一个出现异常就结束任务
    CompletableFuture.anyOf(allCompletableFuture, anyException)
        .whenComplete((value, exception) -> {
          if (Objects.nonNull(exception)) {
            System.out.println("产生异常,提前结束!");
            exception.printStackTrace();
            return;
          }
          System.out.println("所有任务正常完成!");
        });
复制代码

执行结果如下:

completable-future-test-Thread-2开始执行任务:task1
completable-future-test-Thread-3开始执行任务:task2
completable-future-test-Thread-4开始执行任务:task3
正在执行任务task2
正在执行任务task3
正在执行任务task1
task2执行结束
task1执行结束
task3执行结束
产生异常,提前结束!
java.util.concurrent.CompletionException: java.lang.RuntimeException: 任务2异常!
  at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
  at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
  at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: 任务2异常!
  at com.example.awesomerocketmq.completable.CompletableFutureTest.lambda$t$1(CompletableFutureTest.java:53)
  at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
  ... 3 more
​
复制代码

CompletableFuture没有现成的api实现快速失败的功能,所以我们只能结合allOf()anyOf()来逻辑来自定义方法完成快速失败的逻辑;

1.我们需要额外创建一个CompletableFuture来监听所有的CompletableFuture,一旦其中一个CompletableFuture产生异常,我们就设置额外的CompletableFuture立即完成。

2.把所有的CompletableFuture和额外的CompletableFuture放在anyOf()方法中,这样一旦额外的CompletableFuture完成,说明产生异常了;否则就需要等待所有的CompletableFuture完成。

注意

  • 异常处理

最后需要注意的是,所有的CompletableFuture任务一定要加上异常处理:

    CompletableFuture
        // 任务1
        .supplyAsync(
            () -> {
              Thread currentThread = Thread.currentThread();
              String ThreadName = currentThread.getName();
              String taskName = "task1";
              System.out.println(ThreadName + "开始执行任务:" + taskName);
              System.out.println("正在执行任务" + taskName);
              System.out.println(taskName + "执行结束");
              return taskName;
            }, THREAD_POOL_EXECUTOR)
        .whenComplete((v,e)->{
          if(Objects.nonNull(e)){
            // todo
            // 处理异常
          }
          if(Objects.nonNull(v)){
            // todo
          }
        });
复制代码

还可以通过另外两个方法处理:exceptionally()或者handle()

  • 自定义线程池

CompletableFuture默认的线程池是ForkJoinThreadPool,建议大家在使用的时候尽可能地使用自定义线程池,这样方便后续的代码优化以及相关的日志查看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux20 -- 线程安全、保证线程安全的示例代码

一、线程安全 线程安全即就是在多线程运行的时候&#xff0c;不论线程的调度顺序怎样&#xff0c;最终的结果都是 一样的、正确的。那么就说这些线程是安全的。 线程安全&#xff1a;多线程程序&#xff0c;无论调度顺序咋样&#xff0c;都可以得到正确一致的结果。安全–正确…

React-fiber基础之requestAnimationFrame和requestIdleCallback

屏幕刷新率 目前大多数设备的屏幕刷新率是60次每秒浏览器渲染动画或者页面的每一帧的速率也需要根设备屏幕的刷新率保持一致页面是一帧一帧绘制出来的&#xff0c;当每秒的帧数&#xff08;FPS&#xff09;达到60&#xff0c;页面是流畅的&#xff0c;小于这个值&#xff0c;用…

公众号免费查题接口

公众号免费查题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击…

G1D19-DPCS KEAttacKG复现

今天先来看DP 一、DP 1044初稿完成啦~~ 创作与表达的过程总是令人心情愉悦&#xff01; 内容很多&#xff0c;所以效率还算可以啦~但是如果能把工作流程定义地更加清楚的话&#xff0c;效率应该还可以再高一点&#xff01;&#xff01;&#xff01; 二、CS KE 我发现自己网…

Layer2之争:短期看Optimism 长期看zkSync?

今天来说说 Layer2 的 OP 与 ZK 之争问题 短期 OP&#xff0c;长期 ZK&#xff1f; 这句话因为 V 神说过&#xff0c;所以被很多人认为是「金科玉律」&#xff0c;然而实际情况要复杂的多。 常见的那类比较网上已经说烂 - 即 OP 基于博弈 < ZK 基于数学 &#xff1b;O…

[CISCN2019 总决赛 Day2 Web1]Easyweb

扫目录能扫到一个robots.txt 在页面源代码发现 访问image.php.bak拿到源码 <&#xfeff;?php include "config.php";$idisset($_GET["id"])?$_GET["id"]:"1"; $pathisset($_GET["path"])?$_GET["path"]:&…

Plant Communications|高质量的基因组组装和泛基因组研究促进了绿豆的基因发现及其改进

TITLE&#xff1a;High-quality genome assembly and pan-genome studies facilitate genetic discovery in mungbean and its improvement 译名&#xff1a;高质量的基因组组装和泛基因组研究促进了绿豆的基因发现及其改进 期刊&#xff1a;Plant Communications 日期&#xf…

单卡完美复现pan++论文代码结果

源代码 1、根据源代码github地址下载代码&#xff0c;上传到服务器&#xff0c;使用服务器为3090显卡&#xff0c;cuda11.4 2、所需要的包的版本如下&#xff1a; Package Version -------------------------------- ------------------- absl-py …

【附源码】计算机毕业设计JAVA学生信息管理系统2021

【附源码】计算机毕业设计JAVA学生信息管理系统2021 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA…

solvePnP的使用及物理意义

1 PnP问题概述 PnP问题&#xff1a;Perspective-n-Point问题。 参考下图&#xff0c; 给定n个3D空间参考点&#xff0c;以及各点在相机图像上对应的成像点&#xff0c;求参考点所在坐标系与相机的空间关系。 即&#xff1a; 已知条件1&#xff1a;给定匹配点对&#xff1a;世…

LeetCode | 391.完美矩形

给你一个数组 rectangles &#xff0c;其中 rectangles[i] [xi, yi, ai, bi] 表示一个坐标轴平行的矩形。这个矩形的左下顶点是 (xi, yi) &#xff0c;右上顶点是 (ai, bi) 。 如果所有矩形一起精确覆盖了某个矩形区域&#xff0c;则返回 true &#xff1b;否则&#xff0c;返…

基于Matlab的合成孔径雷达模拟陆地场景(附源码)

目录 一、生成模拟地形 二、指定搜救系统和场景 三、定义地表反射率 四、配置雷达收发器 4.1 生成数据多维数据集 4.2 可视化 SAR 数据 五、总结 六、程序 合成孔径雷达&#xff08;SAR&#xff09;系统使用平台运动来模拟更长的孔径&#xff0c;以提高跨距离分辨率。S…

【半监督图像分割】2021-CPS CVPR

文章目录【半监督图像分割】2021-CPS CVPR1. 简介1.1 简介1.2 相关工作1.3 创新2. 网络2.1 网络架构2.2 Loss2.3 实验3. 代码【半监督图像分割】2021-CPS CVPR 论文题目&#xff1a;Semi-Supervised Semantic Segmentation with Cross Pseudo Supervision 中文题目&#xff1a;…

【第四部分 | JavaScript 基础】3:函数、作用域与预解析、对象

目录 | 函数 声明与调用 参数 返回值 arguments&#xff08;JS特有知识点&#xff09; 命名函数 和 匿名函数 | 作用域 全局和局部、JS5没有块级作用域 就近原则&#xff1a;作用域链 | 预解析&#xff08;重要&#xff09; 导论&#xff1a;四种语句位置导致的现象 …

安装TPDSS

TPDSS使用前电脑必须安装jdk。如若jdk安装完毕&#xff0c;则忽略第一步 第一步&#xff1a;jdk jdk安装完毕后控制面板会出现java字样 jdk安装完毕后&#xff0c;配置环境变量进行生效 1、进入到刚才安装时选择的文件夹&#xff0c;选中上方文件的路径&#xff0c;鼠标右键&…

全国各省产业结构协调-高级化、合理化指标(2000-2020年)

全国各省产业结构协调-高级化、合理化指标&#xff08;2000-2020年&#xff09; 1、时间&#xff1a;2000-2020年 2、包括&#xff1a;30个省份不含西藏 3、来源&#xff1a;统计NJ和国家统计J 4、指标包括&#xff1a; 原始数据&#xff1a;地区生产总值(亿元)、就业人员…

【软件测试】资深测试是如何火速入坑的?测试任务艰巨无从下手?

目录&#xff1a;导读一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;如何快速融入项目团队? 熟悉…

leetcode 1926. Nearest Exit from Entrance in Maze(迷宫最近的出口)

Input: maze [[“”,“”,“.”,“”],[“.”,“.”,“.”,“”],[“”,“”,“”,“.”]], entrance [1,2] Output: 1 Explanation: There are 3 exits in this maze at [1,0], [0,2], and [2,3]. Initially, you are at the entrance cell [1,2]. You can reach [1,0] by …

大模型产业化有四个关键,昇腾AI推动“AI+遥感”打了个样

文|智能相对论 作者|夜远风 农业卫星在太空“拍下”地面这张“照片”&#xff0c;地面根据这些图像数据&#xff0c;结合气象情况等&#xff0c;通过AI算法就准确地“算”出了农作物的长势状况&#xff0c;给地方政府、种地农民以参考。 &#xff08;图&#xff1a;农业用地的…

uni-app入门:页面布局之window和tabbar

1.window 2.tabbar 3.全局配置与局部页面配置 前言 每个页面按照结构可以分成三部分:window page tabbar.其中window和tabbar一般比较固定&#xff0c;page是平常业务开展的主要载体&#xff0c;根据业务需求进行页面配置。下面主要讲一下window和tabbar。 1.window…