Java JUC并发编程

news2024/11/25 10:30:50

前言

1、JUC是指有关 java.util.concurrent包以及其子包,这些包都是有关线程操作的包
2、HTTPS服务请求中,WEB服务只负责创建主线程来接收外部的HTTPS请求,如果不做任何处理,默认业务逻辑是通过主线程来做的,如果业务执行时间较长且用户访问量较大的情况,WEB服务在单位时间内所能处理的用户请求就会有限,JUC并发编程的核心就是如何来释放主线成以及通过子线程来批量执行任务
3、JUC并发编程并不能提高执行任务所耗费的时间,但是可以极大的提高WEB容器的吞吐量

案例(模拟下单)

在这里插入图片描述

  • 订单实体类
@Data
public class CreateOrderDto {
    /** 订单id */
    private String orderId;

    /** 库存id */
    private String stockId;

    /** 积分id */
    private String itegralId;

    /** 耗时,单位毫秒 */
    private String time;
}
  • Service代码
@Service
public class OrderTaskService {
    public CreateOrderDto create(CreateOrderDto createOrderDto) {
        createOrderDto.setOrderId("OrderTaskService:" + Thread.currentThread().getName());
        System.out.println("OrderTaskService:"+ JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

@Service
public class StockTaskService {
    public CreateOrderDto operate(CreateOrderDto createOrderDto) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        createOrderDto.setStockId("StockFutureTaskService:" + Thread.currentThread().getName());
        System.out.println("StockFutureTaskService:"+JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

@Service
public class ItegralTaskService {
    public CreateOrderDto operate(CreateOrderDto createOrderDto) {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        createOrderDto.setItegralId("ItegralFutureTaskService:" + Thread.currentThread().getName());
        System.out.println("ItegralFutureTaskService:"+JSONObject.toJSONString(createOrderDto));
        return createOrderDto;
    }
}

同步处理

  • 创建订单
 @GetMapping("createOrderCommon")
 public CreateOrderDto createOrder() {
     long start = System.currentTimeMillis();
     // 本地服务 - 创建订单
     CreateOrderDto createOrderDto = new CreateOrderDto();
     createOrderDto = orderTaskService.create(createOrderDto);
     // 三方服务 - 处理库存
     createOrderDto = stockTaskService.operate(createOrderDto);
     // 三方服务 - 处理积分
     createOrderDto = itegralTaskService.operate(createOrderDto);
     long end = System.currentTimeMillis();

     createOrderDto.setTime("createOrderCommon任务耗时:" + String.valueOf(end - start));
     System.out.println("createOrderCommon主线程处理耗时:" + Thread.currentThread().getName() + " " + createOrderDto.getTime() + "毫秒");
     return createOrderDto;
 }
  • 日志
OrderTaskService{"orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-1"}
StockFutureTaskService{"orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-1","stockId":"StockFutureTaskService:http-nio-0.0.0.0-8801-exec-1"}
ItegralFutureTaskService{"itegralId":"ItegralFutureTaskService:http-nio-0.0.0.0-8801-exec-1","orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-1","stockId":"StockFutureTaskService:http-nio-0.0.0.0-8801-exec-1"}
createOrderCommon主线程处理耗时:http-nio-0.0.0.0-8801-exec-1 createOrderCommon任务耗时:1074毫秒

异步处理(FutureTask)

  • 创建订单
@GetMapping("createOrderFutureTask")
public CreateOrderDto createOrderFutureTask() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    CreateOrderDto createOrderDto = new CreateOrderDto();

    // 本地服务 - 创建订单
    createOrderDto = orderTaskService.create(createOrderDto);

    // 三方服务 - 处理库存 TODO【异步】
    CreateOrderDto finalCreateOrderDto = createOrderDto;
    Callable<CreateOrderDto> callableForStock = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return stockTaskService.operate(finalCreateOrderDto);
        }
    };

    // 三方服务 - 处理积分 TODO【异步】
    CreateOrderDto finalCreateOrderDto1 = createOrderDto;
    Callable<CreateOrderDto> callableForItegral = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return itegralTaskService.operate(finalCreateOrderDto1);
        }
    };

    FutureTask<CreateOrderDto> futureForStock = new FutureTask<>(callableForStock);
    new Thread(futureForStock).start();

    FutureTask<CreateOrderDto> futureForItegral = new FutureTask<>(callableForItegral);
    new Thread(futureForItegral).start();

    createOrderDto.setStockId(futureForStock.get().getStockId()); //TODO 阻塞式获取执行结果,所以会占用web容器的主线程
    createOrderDto.setItegralId(futureForStock.get().getItegralId()); //TODO 阻塞式获取执行结果,所以会占用web容器的主线程

    long end = System.currentTimeMillis();
    createOrderDto.setTime("createOrderFutureTask任务耗时:" + String.valueOf(end - start));
    System.out.println("createOrderFutureTask主线程处理耗时:" + Thread.currentThread().getName() + " " + createOrderDto.getTime() + "毫秒");
    return createOrderDto;
}
  • 日志
OrderTaskService{"orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-2"}
StockFutureTaskService{"itegralId":"ItegralFutureTaskService:Thread-16","orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-2","stockId":"StockFutureTaskService:Thread-15"}
ItegralFutureTaskService{"itegralId":"ItegralFutureTaskService:Thread-16","orderId":"OrderTaskService:http-nio-0.0.0.0-8801-exec-2","stockId":"StockFutureTaskService:Thread-15"}
createOrderFutureTask主线程处理耗时:http-nio-0.0.0.0-8801-exec-2 createOrderFutureTask任务耗时:517毫秒

分析1:通过FutureTask实现多线程并发处理时,如果WEB容器主线程fork出来的子线程没有返回执行结果,那么主线程是会一直阻塞,直到子线程返回结果

使用Callable对FutureTask优化

Asynchronous Requests

DeferredResult和Callable都是为了异步生成返回值提供基本支持。一个请求进来,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,DispatcherServlet就会被再次调用并且处理,以异步产生的方式向请求端响应值。这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量

  • 创建订单
@GetMapping("createOrderCallable")
public Callable<CreateOrderDto> createOrderCallable() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();

    Callable<CreateOrderDto> callable = new Callable<CreateOrderDto>() {
        @Override
        public CreateOrderDto call() throws Exception {
            return createOrderFutureTask();
        }
    };

    long end = System.currentTimeMillis();
    System.out.println("createOrderCallable主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

    return callable;
}
  • 日志
createOrderCallable主线程处理耗时:http-nio-0.0.0.0-8801-exec-7 0毫秒
OrderTaskService{"orderId":"OrderTaskService:MvcAsync2"}
StockFutureTaskService{"itegralId":"ItegralFutureTaskService:Thread-20","orderId":"OrderTaskService:MvcAsync2","stockId":"StockFutureTaskService:Thread-19"}
ItegralFutureTaskService{"itegralId":"ItegralFutureTaskService:Thread-20","orderId":"OrderTaskService:MvcAsync2","stockId":"StockFutureTaskService:Thread-19"}
createOrderFutureTask主线程处理耗时:MvcAsync2 createOrderFutureTask任务耗时:501毫秒

异步处理(CompletableFuture)

  • 自定义线程池对象并纳入容器管理
/**
 * @描述: TODO 自定义线程池来处理异步任务。在方法上添加:@Async("api-asyn-taskExecutor")
 * @作者: lixing lixing_java@163.com
 * @日期 2020/2/19 21:18
 */
@Component
@Configuration
public class ThreadPoolTaskExecutorConfig {
    @Bean("api-asyn-taskExecutor")
    public Executor taskExecutorPool() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix("自定义异步线程池"); // 线程名字前缀
        threadPoolTaskExecutor.setCorePoolSize(5); // 核心线程数
        threadPoolTaskExecutor.setQueueCapacity(10); // 任务队列容量(缓存队列)
        threadPoolTaskExecutor.setMaxPoolSize(10); //线程池维护线程的最大数量,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        threadPoolTaskExecutor.setKeepAliveSeconds(120); // 允许空闲时间,当超过了核心线程数之外的线程在空闲时间到达之后会被销毁
        threadPoolTaskExecutor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); // 任务拒绝策略
        // 关闭线程池时是否等待当前调度任务完成后才开始关闭
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown( true ); // 等待
        threadPoolTaskExecutor.setAwaitTerminationSeconds( 60 ); // 等待时长
        threadPoolTaskExecutor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                // 异常统一处理......发送邮件、发送短信
            }
        });
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}
  • 创建订单
/** 注入自定义线程池 */
@Resource
private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;

@GetMapping("createOrderCompletableFuture")
public CreateOrderDto createOrderCompletableFuture() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();
    
    CreateOrderDto createOrderDto = new CreateOrderDto();

    /** 
     * 异步创建订单
     */
    CompletableFuture<CreateOrderDto> supplyAsyncForOrder = CompletableFuture.supplyAsync(() -> {
        return orderTaskService.create(createOrderDto);
    }, threadPoolTaskExecutorConfig.taskExecutorPool());
    
    /**
     * 订单创建成功后,异步处理库存和积分
     */
    CompletableFuture<CreateOrderDto> thenApplyForOrder = supplyAsyncForOrder.thenApply(createOrderDtoTemp -> {
        CompletableFuture<CreateOrderDto> itegral = CompletableFuture.supplyAsync(() -> {
            return itegralTaskService.operate(createOrderDtoTemp);
        }, threadPoolTaskExecutorConfig.taskExecutorPool());

        CompletableFuture<CreateOrderDto> stock = CompletableFuture.supplyAsync(() -> {
            return stockTaskService.operate(createOrderDtoTemp);
        }, threadPoolTaskExecutorConfig.taskExecutorPool());

        return createOrderDtoTemp;
    });

    long end = System.currentTimeMillis();
    System.out.println("createOrderCompletableFuture主线程处理耗时:" + Thread.currentThread().getName() + " " +(end - start) + "毫秒");

    return thenApplyForOrder.get();
}
  • 日志
createOrderCompletableFuture主线程处理耗时:http-nio-0.0.0.0-8801-exec-2 0毫秒
OrderTaskService{"orderId":"OrderTaskService:自定义异步线程池4"}
StockFutureTaskService{"itegralId":"ItegralFutureTaskService:自定义异步线程池5","orderId":"OrderTaskService:自定义异步线程池4","stockId":"StockFutureTaskService:自定义异步线程池4"}
ItegralFutureTaskService{"itegralId":"ItegralFutureTaskService:自定义异步线程池5","orderId":"OrderTaskService:自定义异步线程池4","stockId":"StockFutureTaskService:自定义异步线程池4"}

异步非阻塞编程(CompletableFuture)

前言

CompletableFuture 是 java.util.concurrent 库在java 8中新增的主要工具
同传统的Future相比:
1、支持流式计算、函数式编程、完成通知、自定义异常处理等新特性。能够主动设置计算的结果值(主动终结计算过程,即completable),从而在某些场景下主动结束阻塞等待
2、而Future由于不能主动设置计算结果值,一旦调用get()进行阻塞等待,要么当计算结果产生,要么超时,才会返回

创建 CompletableFuture 对象

构造函数

/**
 * 通过构造函数创建
 */
@GetMapping("createWayForConstructor")
public String createForConstructor() throws ExecutionException, InterruptedException {
    CompletableFuture<String> completableFuture = new CompletableFuture();
    completableFuture.complete("通过构造函数创建(内部没有包含计算结果),所以需要通过complete来终止计算,否则会一直阻塞在这里");
    return completableFuture.get();
}

supplyAsync(有返回值)和 runAsync(无返回值)

  • 通过supplyAsync创建
@Resource
private ThreadPoolTaskExecutorConfig threadPoolTaskExecutorConfig;
@GetMapping("createWayForSupplyAsync")
public String createWayForSupplyAsync() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();
    CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->{
        try {
            long startForSalve = System.currentTimeMillis();
            Thread.sleep(2000L);
            long endForSalve = System.currentTimeMillis();
            System.out.println(Thread.currentThread().getName() + " 耗时 " + (endForSalve - startForSalve));
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "supplyAsync创建CompletableFuture";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));
    return completableFuture.get();
}
主线程 http-nio-0.0.0.0-8801-exec-1 耗时 4
自定义异步线程池1 耗时 2013
  • 通过runAsync创建
@GetMapping("createWayForRunAsync")
public void createWayForRunAsync() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();
    CompletableFuture.runAsync(new Runnable() {
        @Override
        public void run() {
            long startForSalve = System.currentTimeMillis();
            try {
                Thread.sleep(2000L);
                long endForSalve = System.currentTimeMillis();
                System.out.println(Thread.currentThread().getName() + " 耗时 " + (endForSalve - startForSalve));
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));
}
主线程 http-nio-0.0.0.0-8801-exec-3 耗时 0
自定义异步线程池2 耗时 2003

thenApply / thenAccept / thenRun

thenApply 提交的任务类型需遵从Function签名,也就是有入参和返回值,其中入参为前置任务的结果
thenAccept 提交的任务类型需遵从Consumer签名,也就是有入参但是没有返回值,其中入参为前置任务的结果
thenRun 提交的任务类型需遵从Runnable签名,即没有入参也没有返回值
thenApplyAsync、thenAcceptAsync、thenRunAsync:
带Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的
  • thenApply
@GetMapping("then")
public void then() throws ExecutionException, InterruptedException {
   long startForMaster = System.currentTimeMillis();

   CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
       System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
       return "completableFuture1";
   }, threadPoolTaskExecutorConfig.taskExecutorPool());

   CompletableFuture<String> completableFuture2 = completableFuture1.thenApply((threadName) -> {
       System.out.println(MessageFormat.format("completableFuture2 = {0}", Thread.currentThread().getName()));
       return "completableFuture2";
   });

   long endForMaster = System.currentTimeMillis();
   System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));
}
主线程 http-nio-0.0.0.0-8801-exec-3 耗时 0
completableFuture1 = 自定义异步线程池2
completableFuture2 = 自定义异步线程池2

thenApply / thenAccept / thenRun 连接的2个任务是有前后依赖的,当且仅当前置任务计算完成时才会开始后置任务的计算

  • thenApplyAsync
@GetMapping("then")
public void then() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();

    CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
        return "completableFuture1";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    CompletableFuture<String> completableFuture2 = completableFuture1.thenApplyAsync((threadName) -> {
        System.out.println(MessageFormat.format("completableFuture2 = {0}", Thread.currentThread().getName()));
        return "completableFuture2";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));
}
主线程 http-nio-0.0.0.0-8801-exec-4 耗时 0
completableFuture1 = 自定义异步线程池3
completableFuture2 = 自定义异步线程池4

thenCompose

thenCompose适用于两个任务之间有前后依赖关系,但是连接任务又是独立的CompletableFuture

thenComposeAsync:带Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的
  • thenCompose
@GetMapping("then")
    public String then() throws ExecutionException, InterruptedException {
        long startForMaster = System.currentTimeMillis();

        CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
            return "completableFuture1";
        }, threadPoolTaskExecutorConfig.taskExecutorPool());

        CompletableFuture<String> completableFuture2 = completableFuture1.thenCompose((completableFuture1_r) ->
                CompletableFuture.supplyAsync(() -> {
                    System.out.println(MessageFormat.format("completableFuture2 = {0}", Thread.currentThread().getName()));
                    return completableFuture1_r + ",completableFuture2";
                }, threadPoolTaskExecutorConfig.taskExecutorPool())
        );

        long endForMaster = System.currentTimeMillis();
        System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));

        return completableFuture2.get();
    }
主线程 http-nio-0.0.0.0-8801-exec-1 耗时 0
completableFuture1 = 自定义异步线程池3
completableFuture2 = 自定义异步线程池4

thenCombine

thenCombineAsync:带Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的
  • thenCombine
@GetMapping("then")
public String then() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();

    CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
        return "completableFuture1";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(MessageFormat.format("completableFuture2 = {0}", Thread.currentThread().getName()));
        return "completableFuture2";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    CompletableFuture<String> completableFuture3 = completableFuture1.thenCombine(completableFuture2, (r1, r2) -> r1 + " " + r2);

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));

    return completableFuture3.get();
}
主线程 http-nio-0.0.0.0-8801-exec-2 耗时 0
completableFuture1 = 自定义异步线程池3
completableFuture2 = 自定义异步线程池4

thenCombine连接的两个任务没有依赖关系,前后是并行执行的,只有当两个任务均完成时才会将其结果同时传递给下游处理任务

whenComplete 和 handle

1、whenComplete用于任务完成时的回调通知,解决了传统future在任务完成时无法主动发起通知的问题。前置任务会将计算结果或者抛出的异常作为入参传递给回调通知函数
2、handle与whenComplete的作用类似,handle接收的处理函数有返回值,而且返回值会影响最终获取的计算结果

whenCompleteAsync:带Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的
handleAsync:带Async后缀的函数表示需要连接的后置任务会被单独提交到线程池中,从而相对前置任务来说是异步运行的
  • whenComplete
@GetMapping("then")
public String then() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();

    CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
        System.out.println(0/0);
        return "completableFuture1";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    CompletableFuture<String> completableFuture2 = completableFuture1.whenComplete((r, e) -> {
        if(e != null){
            System.out.println("操作失败:" + e);
        } else {
            System.out.println("操作成功:" + r);
        }
    });

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));

    return completableFuture2.get();
}
主线程 http-nio-0.0.0.0-8801-exec-2 耗时 1
completableFuture1 = 自定义异步线程池2
操作失败:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
  • handle
@GetMapping("then")
public String then() throws ExecutionException, InterruptedException {
    long startForMaster = System.currentTimeMillis();

    CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(MessageFormat.format("completableFuture1 = {0}", Thread.currentThread().getName()));
        System.out.println(0/0);
        return "completableFuture1";
    }, threadPoolTaskExecutorConfig.taskExecutorPool());

    CompletableFuture<String> completableFuture2 = completableFuture1.handle((r, e) -> {
        if(e != null){
            return e.getMessage();
        } else {
            return "completableFuture1";
        }
    });

    long endForMaster = System.currentTimeMillis();
    System.out.println("主线程 " + Thread.currentThread().getName() + " 耗时 " + (endForMaster - startForMaster));

    return completableFuture2.get();
}
主线程 http-nio-0.0.0.0-8801-exec-2 耗时 0
completableFuture1 = 自定义异步线程池2

其它

thenAccepetBoth():两个任务执行完成后,将结果交给thenAccepetBoth处理,无返回值

runAfterBoth():两个任务都执行完成后,执行下一步操作(Runnable类型任务)

applyToEither():两个任务哪个执行的快,就使用哪一个结果,有返回值

acceptEither():两个任务哪个执行的快,就消费哪一个结果,无返回值

runAfterEither():任意一个任务执行完成,进行下一步操作(Runnable类型任务)

allOf():当所有给定的 CompletableFuture 完成时,返回一个新的 CompletableFuture

anyOf():当任何一个给定的CompletablFuture完成时,返回一个新的CompletableFuture

exceptionally:返回一个新的CompletableFuture,当前面的CompletableFuture完成时,它也完成,当它异常完成时,给定函数的异常触发这个CompletableFuture的完成

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

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

相关文章

Linux文件基础IO

目录 C文件IO相关操作 介绍函数 文件相关系统调用接口 接口介绍 fd文件描述符 重定向 缓冲区 inode 软硬链接 动静态库 库的制作 制作静态库 制作动态库 使用库 使用静态库 使用动态库 C文件IO相关操作 介绍函数 打开文件 参数介绍&#xff1a; const char*…

MySQL的explain字段解释

MySQL的explain字段解释 ,type类型含义:1.id 2.select_type 3.table 4.type(重要) 5.possible_keys 6.possible_keys 7. key 8.key_len 9. ref 10. rows(重要) 11. filtered 12. Extra(重要) 如下: Explain命令是查看查询优化器是如何决定执行查询的主要方法。这个功…

Firewalld防火墙详解

文章目录 Firewalld防火墙什么是防火墙Firewalld防火墙的概念Firewalld防火墙运行模式Firewalld防火墙的命令Firewalld防火墙的高级规则 Firewalld防火墙 什么是防火墙 防火墙&#xff1a;防范一些网络攻击。有软件防火墙、硬件防火墙之分。 硬件防火墙和软件防火墙的主要区…

【软件开发】MyBatis 理论篇

MyBatis 理论篇 1.MyBatis 是什么&#xff1f; MyBatis 是一个半 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了 JDBC&#xff0c;开发时只需要关注 SQL 语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。…

初识网络之协议定制

目录 一、数据在网络传输中遇到的部分问题 1. 序列化与反序列化 2. 如何保证接收端读取一个完整的报文 二、实现一个简单的网络计算器 1. 客户端处理 1.1 请求结构体和返回结构体 1.2 解析输入的字符串 1.3 序列化 1.4 添加标识符和字符串长度 1.5 接收服务端返回的数…

浏览器的回流与重绘与事件循环

浏览器的回流与重绘和事件循环 浏览器回流浏览器重绘事件循环 浏览器回流 什么是浏览器回流&#xff1f; 回流是通过JS代码让布局或者几何属性改变&#xff0c;使部分页面或者整个页面更新的过程 浏览器重绘 剩下的是浏览器重绘&#xff1a;比如改变div的visibility, color、…

如何使用Foxmail 7.2.25版本登录Microsoft 365 国内版(即世纪互联版)邮箱

近期微软在全球取消了在Exchange Online 的基本身份验证&#xff0c;取消了之后只有适配微软新式验证的客户端才支持登录&#xff0c;以往的直接配置IMAP/POP服务器地址和邮箱账号密码来登录的方式已经行不通了。 详情可以点击此链接了解&#xff1a;弃用 Exchange Online 中的…

APP性能测试中的几个重要概念,你都知道吗?

目录 前言 一. 内存  二. CPU 三. 流量 四. 电量 五. 启动时间 六. 总结 前言 我们在使用各种 App 的时候基本会关注到&#xff1a;这款软件挺耗流量的&#xff1f;运行起来设备掉电有点快嘛&#xff1f;切换页面的时候还会有卡顿等现象&#xff1f;如果遇到有这些问题…

程序员必看的书籍推荐

程序员必看的书籍推荐&#xff1a; 推荐1&#xff1a;Python 网络数据采集 作者&#xff1a;Ryan Mitchell 译者&#xff1a;陶俊杰&#xff0c;陈小莉 原书4.6星好评&#xff0c;一本书搞定数据采集 涵盖数据抓取、数据挖掘和数据分析 提供详细代码示例&#xff0c;快速解决实…

九、RGBA数据转YUV422存储

1、介绍 将RGBA转换为YUV数据&#xff0c;首先我们是知道有公式是可以将RGBA转换为YUV的&#xff0c;但是图像的每个像素都有一个R、G、B&#xff0c;A值的&#xff0c;但是YUV422(就是两个像素两个Y一个U一个V的)&#xff0c;因此我们还需要将一个像素的RGBA四个值转换为YUV三…

VLAN内容

一、VLAN VLAN是拥有一组共同要求且与物理位置无关的终端设备的逻辑组。 终端设备包括终端用户工作站、服务器、路由器等诸如此类设备。 物理子网由想同物理电缆分段中的设备组成&#xff1b;逻辑子网由相互通信且物理位置无关的设备所组成。VLAN是一种逻辑子网&#xff0c;并…

华为OD机试真题 Java 实现【分糖果】【2022Q2 200分】,附详细解题思路

一、题目描述 小明从糖果盒中随意抓一把糖果&#xff0c;每次小明会取出一半的糖果分给同学们。 当糖果不能平均分配时&#xff0c;小明可以选择从糖果盒中&#xff08;假设盒中糖果足够&#xff09;取出一个糖果或放回一个糖果。 小明最少需要多少次&#xff08;取出、放回…

Sentinel-1(Resolution、Pixel Spacing)

目录 10m&#xff1f;还是20*22m&#xff1f; Resolution和Pixel Spacing 10m&#xff1f;还是20*22m&#xff1f; Sentinel-1 SAR GRD的分辨率为10m&#xff0c;基本上是常识了https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S1_GRD#description…

创建型设计模式06-单例模式

&#x1f9d1;‍&#x1f4bb;作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 单例模式 单例模式是一种创建型设计模式&#xff0c;它的目的是确保一个类只有一个实例&#xff0c;并…

RHCE 作业四

1.dns正向解析 一.初始准备 关闭安全软件安装bind软件 [rootserver ~]# setenforce 0 [rootserver ~]# systemctl stop firewalld [rootserver ~]# yum install bind -y 配置服务端和客户端ip 二.DNS配置 1>服务端编辑bind主配置文件 [rootserver ~]# vim /et…

案例24:基于Springboot旅游景点导游平台系统开题报告

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

六、opengles显示YUV数据

工程文件名为&#xff1a;com.example.threetextureyuv 1、yuv回顾 1&#xff09;yuv的由来 是在保持图片质量的前提下降低图片内存大小提供传输效率&#xff0c;并且传统的BGR格式 对于黑白图片不支持亮度的调节。 Y”表示明亮度&#xff08;Luminance、Luma&#xff09;&…

javascript基础二十九:JavaScript如何判断一个元素是否在可视区域中?

一、用途 可视区域即我们浏览网页的设备肉眼可见的区域&#xff0c;如下图 在日常开发中&#xff0c;我们经常需要判断目标元素是否在视窗之内或者和视窗的距离小于一个值&#xff08;例如 100 px&#xff09;&#xff0c;从而实现一些常用的功能&#xff0c;例如&#xff1a;…

路径规划算法:基于和声优化的路径规划算法- 附代码

路径规划算法&#xff1a;基于和声优化的路径规划算法- 附代码 文章目录 路径规划算法&#xff1a;基于和声优化的路径规划算法- 附代码1.算法原理1.1 环境设定1.2 约束条件1.3 适应度函数 2.算法结果3.MATLAB代码4.参考文献 摘要&#xff1a;本文主要介绍利用智能优化算法和声…

chatgpt赋能python:Python如何倒序循环

Python如何倒序循环 在Python编程中&#xff0c;倒序循环是一种常见的操作。有时候我们需要倒序遍历一个序列&#xff0c;以便获取最后一个元素或在某些情况下需要运算。Python提供了多种方法来实现倒序循环。在本文中&#xff0c;我们将介绍Python中可用的不同循环遍历方法&a…