• jdk 8-future 异步处理-轮询isDone判断完成-springBoot示例


    Future接口用于获取异步计算的结果,可通过get()获取结果、cancel()取消、isDone()判断是否完成等操作。
    V get(): 获取结果,若无结果会阻塞至异步计算完成
    V get(long timeOut, TimeUnit unit):获取结果,超时返回null
    boolean isDone():执行结束(完成/取消/异常)返回true
    boolean isCancelled():任务完成前被取消返回true
    boolean cancel(boolean mayInterruptRunning):取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程
    java的Timer类来进行定时调用,schedule(TimerTask task, long delay)调度一个task,经过delay(ms)后开始进行调度,仅仅调度一次

    参考

    在 Java Future 上使用带有 isDone 和 Cancel 的轮询而不是阻塞 get
    Future的isDone()方法结果为true代表了什么?
    java Timer(定时调用、实现固定时间执行)
    Java之Future(cancel,iSDone)
    Java多线程任务超时结束的5种实现方法

    操作

    pom

        
        <parent>
            <artifactId>spring-boot-starter-parentartifactId>
            <groupId>org.springframework.bootgroupId>
            <version>2.3.1.RELEASEversion>
            <relativePath/>
        parent>
        <properties>
            <maven.compiler.source>8maven.compiler.source>
            <maven.compiler.target>8maven.compiler.target>
            <java.version>1.8java.version>
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
            <skipTests>trueskipTests>
    
            <spring-boot.version>2.3.1.RELEASEspring-boot.version>
        properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starterartifactId>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    java测试代码

    package ***.cfg;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.core.task.AsyncTaskExecutor;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    
    import javax.annotation.PostConstruct;
    import java.util.*;
    import java.util.concurrent.Future;
    import java.util.concurrent.ThreadLocalRandom;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * ExecutorPoolConfig 类说明:
     *
     * @author z.y
     * @version v1.0
     * @date 2022/7/27
     */
    @Order
    @Configuration
    public class ExecutorPoolConfig {
        private static final Logger log = LoggerFactory.getLogger(ExecutorPoolConfig.class);
        @Bean ("taskNotifyThread")
        public AsyncTaskExecutor taskNotifyThread(){
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(1);
            executor.setMaxPoolSize(10);
            executor.setKeepAliveSeconds(60);
            executor.setQueueCapacity(1);
            executor.setThreadNamePrefix("TASK-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
            executor.initialize();
            log.info("线程池-初始化");
            return executor;
        }
        public void testPool(int size){
            try {
                size = Math.max(4,size);
                log.info("开始测试 线程池,数量 {}",size);
                AsyncTaskExecutor executor = taskNotifyThread();
                Map<String, Future<Boolean>> map = new HashMap<>(size);
                for (int i = 0; i < size; i++) {
                    int index = i;
                    map.put("T-"+index,executor.submit(() -> testOne(index)));
                }
                log.info("任务 已提交 线程池,数量 {}",size);
                Set<String> keySet = map.keySet();
                List<String> doneList = new ArrayList<>(size);
                // 方式1 使用 get 阻塞判断否结束--------------
                Boolean b;
                for (String key : keySet) {
                    b = map.get(key).get();
                    if(null != b){
                        log.info("get 阻塞判断是否结束,{}",b);
                        doneList.add(key);
                    }
                }
                // 方式1 使用 get 阻塞判断是结束--------------
    
                // 方式2 使用 isDone 轮询判断是结束---------------
                Future<Boolean> future;
                while (keySet.size() != doneList.size()){
                    for (String key : keySet) {
                        // 已结束的子任务 直接跳过
                        if(doneList.contains(key)){ continue; }
                        future = map.get(key);
                        if(future.isDone()){
                            doneList.add(key);
                            log.info("子任务已结束,{},{}",key,future.get());
                        }else{
                            log.info("子任务未结束-===-{}",key);
                        }
                    }
                    log.info("问了一遍,结束的任务:{},先睡1s",doneList);
                    TimeUnit.SECONDS.sleep(1);
                }
                // 方式2 使用 isDone 轮询判断是结束---------------
                log.info("都已经处理结束,结束的顺序:{}",doneList);
            }catch (Exception e){
                log.error("任务测试失败",e);
            }
        }
        private Boolean testOne(int index){
            try {
                int i = ThreadLocalRandom.current().nextInt(2,10);
                log.info("子任务-{}-开始,睡眠:{}",index,i);
                TimeUnit.SECONDS.sleep(i);
                log.info("子任务-{}=====结束,睡眠:{}",index,i);
                return i % 2 == 0;
            }catch (Exception e){
                log.error("子任务测试失败",e);
            }
            return false;
        }
        @PostConstruct
        public void init(){
            log.info("配置类初始化完成");
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    log.info("线程池开始处理");
                    testPool(6);
                }
            }, 2000);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    在这里插入图片描述

    日志

    方式2-轮询处理

    2022-07-28 08:54:55.722  INFO 17952 --- [           main] ***.cfg.ExecutorPoolConfig  : 线程池-初始化
    2022-07-28 08:54:55.723  INFO 17952 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'taskNotifyThread'
    2022-07-28 08:54:55.992  INFO 17952 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9980 (http) with context path '/runner-demo'
    2022-07-28 08:54:56.002  INFO 17952 --- [           main] ***.RunnerSpringDemoApplication      : Started RunnerSpringDemoApplication in 2.32 seconds (JVM running for 4.784)
    2022-07-28 08:54:56.004  INFO 17952 --- [           main] ***.RunnerSpringDemoApplication      : http://127.0.0.1:9980/runner-demo/
    2022-07-28 08:54:57.714  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 线程池开始处理
    2022-07-28 08:54:57.714  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 开始测试 线程池,数量 6
    2022-07-28 08:54:57.715  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 任务 已提交 线程池,数量 6
    2022-07-28 08:54:57.716  INFO 17952 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-4-开始,睡眠:2
    2022-07-28 08:54:57.716  INFO 17952 --- [         TASK-3] ***.cfg.ExecutorPoolConfig  : 子任务-3-开始,睡眠:3
    2022-07-28 08:54:57.716  INFO 17952 --- [         TASK-5] ***.cfg.ExecutorPoolConfig  : 子任务-5-开始,睡眠:9
    2022-07-28 08:54:57.716  INFO 17952 --- [         TASK-1] ***.cfg.ExecutorPoolConfig  : 子任务-0-开始,睡眠:2
    2022-07-28 08:54:57.716  INFO 17952 --- [         TASK-2] ***.cfg.ExecutorPoolConfig  : 子任务-2-开始,睡眠:4
    2022-07-28 08:54:57.716  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-2
    2022-07-28 08:54:57.716  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:54:57.716  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-4
    2022-07-28 08:54:57.716  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-3
    2022-07-28 08:54:57.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:54:57.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-0
    2022-07-28 08:54:57.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[],先睡1s
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-2
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-4
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-3
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-0
    2022-07-28 08:54:58.717  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[],先睡1s
    2022-07-28 08:54:59.717  INFO 17952 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-4=====结束,睡眠:2
    2022-07-28 08:54:59.717  INFO 17952 --- [         TASK-1] ***.cfg.ExecutorPoolConfig  : 子任务-0=====结束,睡眠:2
    2022-07-28 08:54:59.717  INFO 17952 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-1-开始,睡眠:4
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-2
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-4,true
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-3
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-0,true
    2022-07-28 08:54:59.718  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0],先睡1s
    2022-07-28 08:55:00.717  INFO 17952 --- [         TASK-3] ***.cfg.ExecutorPoolConfig  : 子任务-3=====结束,睡眠:3
    2022-07-28 08:55:00.719  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-2
    2022-07-28 08:55:00.719  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:55:00.719  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-3,false
    2022-07-28 08:55:00.719  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:00.719  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3],先睡1s
    2022-07-28 08:55:01.716  INFO 17952 --- [         TASK-2] ***.cfg.ExecutorPoolConfig  : 子任务-2=====结束,睡眠:4
    2022-07-28 08:55:01.720  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-2,true
    2022-07-28 08:55:01.720  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:55:01.720  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:01.720  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2],先睡1s
    2022-07-28 08:55:02.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-1
    2022-07-28 08:55:02.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:02.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2],先睡1s
    2022-07-28 08:55:03.718  INFO 17952 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-1=====结束,睡眠:4
    2022-07-28 08:55:03.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-1,true
    2022-07-28 08:55:03.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:03.721  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
    2022-07-28 08:55:04.722  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:04.722  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
    2022-07-28 08:55:05.723  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务未结束-===-T-5
    2022-07-28 08:55:05.723  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1],先睡1s
    2022-07-28 08:55:06.716  INFO 17952 --- [         TASK-5] ***.cfg.ExecutorPoolConfig  : 子任务-5=====结束,睡眠:9
    2022-07-28 08:55:06.723  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 子任务已结束,T-5,false
    2022-07-28 08:55:06.723  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 问了一遍,结束的任务:[T-4, T-0, T-3, T-2, T-1, T-5],先睡1s
    2022-07-28 08:55:07.724  INFO 17952 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 都已经处理结束,结束的顺序:[T-4, T-0, T-3, T-2, T-1, T-5]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    方式1-阻塞获取

    --2022-07-29 10:19:25.436 - INFO 19288 --- [           main] ***.cfg.ExecutorPoolConfig  : 线程池-初始化
    --2022-07-29 10:19:25.438 - INFO 19288 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'taskNotifyThread'
    --2022-07-29 10:19:25.819 - INFO 19288 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9980 (http) with context path '/runner-demo'
    --2022-07-29 10:19:25.831 - INFO 19288 --- [           main] ***.RunnerSpringDemoApplication      : Started RunnerSpringDemoApplication in 3.103 seconds (JVM running for 6.078)
    --2022-07-29 10:19:25.834 - INFO 19288 --- [           main] ***.RunnerSpringDemoApplication      : http://127.0.0.1:9980/runner-demo/
    --2022-07-29 10:19:27.424 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 线程池开始处理
    --2022-07-29 10:19:27.424 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 开始测试 线程池,数量 6
    --2022-07-29 10:19:27.427 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 任务 已提交 线程池,数量 6
    --2022-07-29 10:19:27.427 - INFO 19288 --- [         TASK-5] ***.cfg.ExecutorPoolConfig  : 子任务-5-开始,睡眠:6
    --2022-07-29 10:19:27.427 - INFO 19288 --- [         TASK-2] ***.cfg.ExecutorPoolConfig  : 子任务-2-开始,睡眠:2
    --2022-07-29 10:19:27.427 - INFO 19288 --- [         TASK-1] ***.cfg.ExecutorPoolConfig  : 子任务-0-开始,睡眠:7
    --2022-07-29 10:19:27.427 - INFO 19288 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-4-开始,睡眠:2
    --2022-07-29 10:19:27.427 - INFO 19288 --- [         TASK-3] ***.cfg.ExecutorPoolConfig  : 子任务-3-开始,睡眠:7
    --2022-07-29 10:19:29.429 - INFO 19288 --- [         TASK-2] ***.cfg.ExecutorPoolConfig  : 子任务-2=====结束,睡眠:2
    --2022-07-29 10:19:29.429 - INFO 19288 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-4=====结束,睡眠:2
    --2022-07-29 10:19:29.429 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,true
    --2022-07-29 10:19:29.429 - INFO 19288 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-1-开始,睡眠:3
    --2022-07-29 10:19:32.430 - INFO 19288 --- [         TASK-4] ***.cfg.ExecutorPoolConfig  : 子任务-1=====结束,睡眠:3
    --2022-07-29 10:19:32.430 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,false
    --2022-07-29 10:19:32.431 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,true
    --2022-07-29 10:19:33.429 - INFO 19288 --- [         TASK-5] ***.cfg.ExecutorPoolConfig  : 子任务-5=====结束,睡眠:6
    --2022-07-29 10:19:34.429 - INFO 19288 --- [         TASK-1] ***.cfg.ExecutorPoolConfig  : 子任务-0=====结束,睡眠:7
    --2022-07-29 10:19:34.429 - INFO 19288 --- [         TASK-3] ***.cfg.ExecutorPoolConfig  : 子任务-3=====结束,睡眠:7
    --2022-07-29 10:19:34.429 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,false
    --2022-07-29 10:19:34.429 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,true
    --2022-07-29 10:19:34.429 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : get 阻塞判断是否结束,false
    --2022-07-29 10:19:34.429 - INFO 19288 --- [        Timer-0] ***.cfg.ExecutorPoolConfig  : 都已经处理结束,结束的顺序:[T-2, T-1, T-4, T-3, T-5, T-0]
    -
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    结束

    get阻塞方式,任务不是最早结束的就能得到结果值,是按照自定义key的 顺序获取结果。
    isDone轮询方式,任务是在主线程循环判断,结果顺序是和任务耗时有关,代码较多。
    个人见解,如有问题请指出。

  • 相关阅读:
    《第一堂棒球课》:王牌右外野·棒球9号位
    plt函数显示图片 & 在图片上画边界框 & 边界框坐标转换
    快速排序与归并排序的链式实现(golang)
    玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一)
    kubernetes集群搭建
    vue学习之条件渲染
    Python3常用其他API速查手册(持续更新ing...)
    jquery ajax跨域解决方法(json方式)
    Matlab论文插图绘制模板第117期—气泡云图
    助力智能化公路养护,基于YOLOv5s集成SPD-BIFPN-SE开发构建公路开裂检测识别系统
  • 原文地址:https://blog.csdn.net/privateobject/article/details/126049344