CompletableFuture

线程基础知识复习

大神:Doug Lea

java.util.concurrent

java.util.concurrent.aomic

Java.util.concurrent.locks

硬件

摩尔定律:

它是由英特尔创始人之一 Gordon Moore(戈登·摩尔)提出来的。其内容:

当价格不变是,集成电路上可容纳的元器件的数目每隔18-24个月会增加一倍,性能也将提升一倍

换言之,每一美元所能买到的电脑性能,将每隔18-24个月翻一倍以上。这一定路揭示了信息技术进步的速度

可是从2003年开始cpu主频不再更新,而是采用多核而不是更快的主频

在主频不再提高而且核数不断增加的情况下,要想让程序更快就要用到并行或兵法编程

start线程

private native void start0();

java语言本身底层语言是C++

底层C++源码解读

​ thread.c

​ java线程是通过start方法启动执行的,主要内容在native方法start0中

​ openjdk的写JNI一般是一一对应的,Thread.java对应的就是Thread.c

​ start0其实就是JVM_StartThread.此时查看源代码可以看到jvm.h中找到声明,jvm.cpp中有实现

​ jvm.cpp

​ 调用操作系统

​ thread.cpp

​ 调用操作系统

多线程概念

1把锁

synchronized

2个并

并发 concurrent

​ 同一实体,同一机器,同时处理多个任务

​ 同一时刻,只有一个事件发生

并行 parallel

​ 不同实体,多台机器,同时处理多个任务

​ 同一时刻,多个任务互不干扰

3 个程

进程:系统中运行的一个应用程序

线程:轻量级进程,在同一个进程会有1个或多个线程

管程:monitor一种同步机制

​ 同步指令

​ java虚拟机可以支持方法级的同步和方法内部一段指令列的同步,这两种同步结构都是使用管程(Monitor,更常见的是直接将它称为锁)来实现

​ 方法级的同步是隐式的,无须通过字节码指令来控制,它实现在方法调用和返回操作之中。虚拟机可以从方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否被声明为同步方法。当方法调用时,调用指令将会检查方法ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期间,执行线程池有了管程,其他任何线程都无法在获取到同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法边界之外时自动释放

​ 同步一段指令集序列通常是由java语言的synchronized语句快来表示的,java虚拟机的指令集中有monitorenter和nonitorexit两条指令来支持synchronized关键字的语义,正确实现synchronized关键字需要javac编译器与java虚拟机两者共同协调支持

用户线程和守护线程

java线程范围两种:用户线程和守护线程

一般情况下不做任何特殊说明配置,默认都是用户线程

用户线程:系统工作线程

守护线程:是一种特殊的线程为其他线程服务的,在后台默默的完成一些系统性的服务

​ 比如垃圾回收线程就是一个最典型的例子

判断是否是守护线程:isDaemon()

设置线程类型:setDemon()

总结

如果用户线程全部结束意味着程序需要完成的业务操作已经结束,守护线程随着JVM一同结束工作

setDaemon(true) 方法必须在start() 之前设置,否则IllegalThreadStateException异常

CompletableFuture

Future

Future 接口定义了操作异步任务执行的一些方法,如获取异步任务的执行结果、取消任务执行、判断任务是否取消、判断任务是否完毕等。

常用FutureTask异步任务

请添加图片描述

java5新加的一个接口,它提供了一种一步并行计算的功能

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String> task = new FutureTask<>(() -> {
        Thread.sleep(5000);
        return "hello";
    });
    Thread thread = new Thread(task, "task");
    thread.start();
    String s = task.get();
    System.out.println(s);
}

优缺点

public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*long start = System.currentTimeMillis();
        TimeUnit.MILLISECONDS.sleep(500);
        TimeUnit.MILLISECONDS.sleep(200);
        TimeUnit.MILLISECONDS.sleep(300);
        long stop = System.currentTimeMillis();
        long time = stop - start;
        System.out.println("耗时"+time);*/

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();

        FutureTask<String> f1 = new FutureTask<String>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(5000);
                return "f1";
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        threadPool.submit(f1);
        FutureTask<String> f2 = new FutureTask<String>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(2000);

                return "f2";
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        threadPool.submit(f2);
        FutureTask<String> f3 = new FutureTask<String>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(3000);
                return "f3";
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        threadPool.submit(f3);


        TimeUnit.MILLISECONDS.sleep(500);
        long stop = System.currentTimeMillis();
        long time = stop - start;
//        f1.get() // 过时抛出异常 
        System.out.println("耗时" + time + "   "+f1.get()+" "+f2.get()+" "+f2.get()); // get操作    这样会导致程序阻塞,一般放在祝线程之后
        System.out.println("main 忙其他任务");

        threadPool.shutdown();
// 放在最后处理:while true:不断询问,isDone,如果运行完拿到结果,否则,等待两秒

    }
}

缺点:耗时cpu资源(Future对与结果的获取不是很友好,需要轮询和询问)

想要复杂任务

简单任务Future完全ok

回调通知

创建异步任务(Future+线程池)

多任务前后依赖可以组合使用

CompletableFuture

为什么会出现?

get()方法在Future计算完成之前会一直处于阻塞状态下

isDone()方法容易耗费cpu资源

对于真正异步处理我们希望可以通过传入回调函数,在Future结束时自动调用该回调函数,这样,我们就不用等待结果

阻塞的方式和异步编程的设计理念相违背,而轮询的方式会耗费无谓的cpu资源。因此

JDK8设计出CompletableFuture

源码

CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成之后通知监听一方

请添加图片描述

CompetionStage

  • CompletionStage代表异步计算过程中的某个阶段,一个阶段完成之后可能会触发另外一个阶段

  • 一个阶段的计算可以是Function,Consume或者Runnable。比如

    ​ stage.thenApply(x->square(x))

    ​ .thenAccept(x->System.out.print(x))

    ​ .thenRun(()->Sysem.out.println())

  • 一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发

代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数

CompletableFuture

  • 在java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture的方法
  • 他可能代表一个明确完成Future,也可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作
  • 它实现了Future和CompletionStage接口

使用

CompletableFuture不推荐使用空参数构造使用

使用静态方法:

runAsync 无返回值

supplyAsync 有返回值

没有指定Executor的方法,直接使用默认的ForkJoinPool.commonPool()作为他线程池执行的异步代码

如果指定线程池,则使用我们自定义的或者特被指定的线程池异步代码

减少阻塞和轮询

 public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService poolExecutor = Executors.newFixedThreadPool(3);
        try {
            // 减少阻塞和轮询
            CompletableFuture<String> cf = CompletableFuture.supplyAsync(
                    () -> {
                        try {
                            TimeUnit.SECONDS.sleep(5);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        System.out.println(10/0);
                        return "hello";
                    }
                    , poolExecutor);
            cf.whenComplete((s, e) -> {
                if (e == null) {
                    System.out.println("whenComplete");

                }
            }).exceptionally(e -> {
                System.out.println("有异常");
                return null;
            });


            System.out.println("main 去忙其他任务");

        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            poolExecutor.shutdown();

        }
    }

请添加图片描述请添加图片描述

get和join区别:

get:编译报异常

join:编译无异常

电商

public class CompletableFutureMallDemo {
    static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("dangdang"),
            new NetMall("taobao")
    );

    /**
     * 中规中矩查法
     * @param list
     * @param productName
     * @return
     */
    public static List<String> getPrice(List<NetMall> list, String productName) {
        return list.stream()
                .map(netMall ->
                        String.format(productName + " in %s price is %.2f",
                                netMall.getNetMallName(), netMall.calcPrice(productName))
                ).collect(Collectors.toList());
    }

    /**
     * Stream配合CompletableFuture查法
     * @param list
     * @param productName
     * @return
     */
    public static List<String> getPriceByCompletableFuture(List<NetMall> list, String productName) {
        return list.stream().map(netMall ->
                CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
                        netMall.getNetMallName(), netMall.calcPrice(productName))))
                .collect(Collectors.toList())
                .stream()
                .map(s -> s.join())
                .collect(Collectors.toList());
    }


    public static void main(String[] args) {
        long startTime1 = System.currentTimeMillis();
        List<String> list1 = getPrice(list, "mysql");
        list1.forEach(System.out::println);
        long endTime1 = System.currentTimeMillis();
        System.out.println(endTime1 - startTime1);

        System.out.println("------------------------");
        long startTime2 = System.currentTimeMillis();
        List<String> list2 = getPriceByCompletableFuture(list, "mysql");
        list1.forEach(System.out::println);
        long endTime2 = System.currentTimeMillis();
        System.out.println(endTime2 - startTime2);




    }


}

class NetMall {
    @Getter
    private String netMallName;

    public NetMall(String netMallName) {
        this.netMallName = netMallName;
    }

    public double calcPrice(String productName) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }

}

常用方法

  1. 获得结果和触发计算

    • 获得结果

      • get()

      • get(long timeout,TimeUnit unit)

      • join()

      • getNow(T valueifAbsent)

        public static void main(String[] args) {
            CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
               /* try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
                return "fsfs";
            });
            String hehe = cf.getNow("hehe");
            System.out.println(hehe);
        }
        
    • 主动触发计算

      • complete
       public static void main(String[] args) {
              CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
                  /*try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }*/
                  return "fsfs";
              });
      //        String hehe = cf.getNow("hehe");
      //        System.out.println(hehe);
      
               try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              // 如果此时完成计算 false  计算的值
              // 否则           true    xxxxx
              System.out.println(cf.complete("xxxxx") + " " + cf.join());
          }
      
  2. 对计算结果进行处理

    ​ 计算结果存在依赖关系,这两个线程串形化

    • thenApply

      由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停

      public static void main(String[] args) {
              ExecutorService threadPool = Executors.newFixedThreadPool(3);
              CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(() -> {
                  try {
                      TimeUnit.SECONDS.sleep(1);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
      
                  return 0;
              },threadPool).thenApply(num -> {
                  num++;
                  System.out.println(num);
                  System.out.println(10 / 0);
                  return num;
              }).thenApply(num -> {
                  num++;
                  System.out.println(num);
                  return num;
              }).thenApply(num -> {
                  num++;
                  System.out.println(num);
                  return num;
              }).whenComplete((num, throwable) -> {
                  System.out.println(num);
              }).exceptionally(throwable -> {
                  System.out.println("异常");
                  return null;
              });
              System.out.println(Thread.currentThread().getName() + " thread");
              threadPool.shutdown();
          }
      
    • handle

      有异常也可以往下一步走,根据带的异常参数可以进行一步处理

      public static void main(String[] args) {
          ExecutorService threadPool = Executors.newFixedThreadPool(3);
          CompletableFuture<Integer> cf = CompletableFuture.supplyAsync(() -> {
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
      
              return 0;
          },threadPool).handle((num,e) -> {
              num++;
              System.out.println(num);
              System.out.println(10 / 0);
              return num;
          }).handle((num,e) -> {
              num++;
              System.out.println(num);
              return num;
          }).handle((num,e)-> {
              num++;
              System.out.println(num);
              return num;
          }).whenComplete((num, throwable) -> {
              System.out.println(num);
          }).exceptionally(throwable -> {
              System.out.println("异常");
              return null;
          });
          System.out.println(Thread.currentThread().getName() + " thread");
          threadPool.shutdown();
      }
      
  3. 对计算结果进行消费

    thenAccept:接受任务的处理结果,并消费处理,无返回结果

    总结:

    ​ thenRun:无参无返回

    ​ thenAccept:有参数无返回值

    ​ thenApply:有参数有返回值

    与线程池之间的关系:

    • 没有传入自定义的线程池,都用默认线程池ForkJoinPool

    • 传入了一个自定线程池

      如果您执行第一个任务的时候,传入了一个自定义线程池

      调用thenRun方法执行第二个任务时,则第二个任务和第一个任务共用同一个线程池

      调用thneRunAsync执行第二个任务时,则第一个任务使用的是您自己传入的线程池,第二个任务使用的ForkJoin线程池

    • 备注:有可能处理太快,系统优化原则,直接使用main线程处理

      其他如:thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,他们之间的区别也是同理

  4. 对计算速度进行选用

    ​ applyToEither:谁执行的快取谁的结果

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello";
        },threadPool);
    
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "word";
        },threadPool);
    
    
        CompletableFuture<String> future = f1.applyToEither(f2, f -> {
            return f + " is winer";
        });
    
        String join = future.join();
        System.out.println(join);
    
        threadPool.shutdown();
    }
    
  5. 对计算结果进行合并

    thenCombine

    两个CompletionStage任务都完成后,最终能把两个任务的结果一起交个thenCombine来处理先完成的等着,等待其他分支任务

    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "hello";
        },threadPool);
    
        CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "world";
        },threadPool);
    
        CompletableFuture<String> future = f1.thenCombine(f2, (x, y) -> x + y);
    
    
        String join = future.join();
        System.out.println(join);
    
        threadPool.shutdown();
    }
    

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python相对路径与绝对路径

9.1 Python 绝对路径与相对路径 - 知乎 (zhihu.com) 目录 1. 绝对路径 1.1 概念 1.2 用绝对路径打开文件 1.2 相对路径 1.3 python路径表示的斜杠问题 1. 绝对路径 1.1 概念 绝对路径 指完整的描述文件位置的路径。绝对路径就是文件或文件夹在硬盘上的完整路径。 在 Win…

Java 基础入门篇(二)—— Java 基础语法

文章目录 一、注释二、字面量三、变量3.1 变量概述3.2 变量在计算机中的底层原理 四、数据类型五、关键字、标志符六、类型转换6.1 自动类型转换6.2 表达式的自动类型转换6.3 强制类型转换 七、运算符7.1 基本算数运算符7.2 符号做连接符7.3 自增自减运算符7.4 赋值运算符7.5 …

基于simulink采用 QSHB 和 HBPS 算法的混合 MIMO 波束成形仿真

一、前言 本例展示了多输入多输出 &#xff08;MIMO&#xff09; 无线通信系统的 Simulink 模型。无线系统使用混合波束成形技术来提高系统吞吐量。 二、介绍 5G和其他现代无线通信系统广泛使用MIMO波束成形技术进行信噪比&#xff08;SNR&#xff09;增强和空间复用&#xff0…

Netty(2)

Netty 文章目录 Netty4 Netty 模型4.1 Netty 模型介绍4.2 Netty demo4.3 Netty 异步模型4.3.1 基本介绍4.3.2 异步模型4.3.3 Future-Listener 机制4.4 Netty 任务队列 task 4 Netty 模型 4.1 Netty 模型介绍 Netty 线程模式&#xff1a;Netty 主要基于主从 Reactor 多线程模型…

开放式基金净值估算数据 API 数据接口

开放式基金净值估算数据 API 数据接口 全量基金数据&#xff0c;实时数据&#xff0c;所有基金数据。 1. 产品功能 返回实时开放式基金净值估值可定义所有基金估值数据&#xff1b;多个基金属性值返回&#xff1b;多维指标&#xff0c;一次查询毫秒级返回&#xff1b;数据持续…

全球5G市场最新进展及未来展望

从智慧医疗到万物互联&#xff0c;从无人驾驶到关乎我国未来发展的“新基建”&#xff0c;自2019年全球5G商用启动后&#xff0c;5G就步入了发展“快车道”;2022年继续保持快速稳定的增长态势&#xff0c;在网络建设、人口覆盖、终端形态等方面发展势头强劲&#xff0c;在技术标…

【致敬未来的攻城狮计划】— 连续打卡第二十三天:RA2E1的存储器基础知识

系列文章目录 1.连续打卡第一天&#xff1a;提前对CPK_RA2E1是瑞萨RA系列开发板的初体验&#xff0c;了解一下 2.开发环境的选择和调试&#xff08;从零开始&#xff0c;加油&#xff09; 3.欲速则不达&#xff0c;今天是对RA2E1 基础知识的补充学习。 4.e2 studio 使用教程 5.…

每天一道算法练习题--Day18 第一章 --算法专题 --- ----------前缀树

前缀树 字典树也叫前缀树、Trie。它本身就是一个树型结构&#xff0c;也就是一颗多叉树&#xff0c;学过树的朋友应该非常容易理解&#xff0c;它的核心操作是插入&#xff0c;查找。删除很少使用&#xff0c;因此这个讲义不包含删除操作。 截止目前&#xff08;2020-02-04&a…

基于R语言APSIM模型应用

随着数字农业和智慧农业的发展&#xff0c;基于过程的农业生产系统模型在模拟作物对气候变化的响应与适应、农田管理优化、作物品种和株型筛选、农田固碳和温室气体排放等领域扮演着越来越重要的作用。APSIM (Agricultural Production Systems sIMulator)模型是世界知名的作物生…

净利润下滑13%,帅丰电器已掉队?

近年来&#xff0c;随着市场竞争加剧&#xff0c;厨电行业加速洗牌&#xff0c;超60%杂牌或被淘汰出局&#xff0c;三类品牌全部被清退。而作为毛利最高的厨电细分市场&#xff0c;集成灶行业吸引了大批企业涌入&#xff0c;市场渗透率快速提升&#xff0c;已经超过30%&#xf…

华为MPLS跨域——后门链路实验配置

目录 配置PE与CE设备对接命令&#xff08;通过OSPF对接&#xff09; 配置后门链路 可以使用任意方式来跑跨域MPLS&#xff08;A、B、C1、C2都可以&#xff09;&#xff0c;不过关于传递Vpnv4路由的配置此处不做介绍&#xff1b;此处只介绍关于PE和CE对接的配置和关于后门链路…

数据存储系统概要

可靠、可扩展与可维护性 现在有很多都属于数据密集型&#xff0c;而不是计算密集型。对于这些类型应用&#xff0c;CPU的处理能力往往不是第一限制性因素&#xff0c;关键在于数据量、数据的复杂度及数据的快速多边形。 数据密集型应用模块&#xff1a; 数据库&#xff1a;存…

对标世界一流|从Just in time到Just in case ——汽车行业供应链管理经验借鉴

01 丰田汽车精益生产 作为最复杂和最成熟的供应链之一&#xff0c;汽车行业供应链无疑是供应链领域集大成者&#xff0c;而提起汽车行业供应链&#xff0c;就不得不提到丰田汽车&#xff1b;提到丰田汽车&#xff0c;就肯定离不开大名鼎鼎的精益生产以及JIT模式。 JIT模式由丰…

云服务器部署python项目

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

SpringBoot+Shiro+Jwt+Vue+elementUI实现前后端分离单体系统Demo

记录一下使用SpringBoot集成Shiro框架和Jwt框架实现前后端分离Web项目的过程&#xff0c;后端使用SpringBoot整合ShiroJwt(auth0)&#xff0c;前端使用vueelementUI框架&#xff0c;前后端的交互使用的是jwt的token&#xff0c;shiro的会话关闭&#xff0c;后端只需要使用Shiro…

在Linux上搭建gitlab以及自动化编译部署的完整流程

一、安装gitlab 首先下载gitlab的安装包&#xff0c;地址如下&#xff1a; https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/ubuntu/pool/bionic/main/g/gitlab-ce/ 然后安装下载的包即可&#xff0c;一般还需要安装openssh-server等依赖包&#xff0c;在安装gitlab包之前可以…

睡眠经济2.0时代来了,老巨头们有护城河吗?

在第23个世界睡眠日&#xff0c;中国睡眠研究会等机构发布了《中国睡眠研究报告2023》&#xff0c;近半数人每晚平均睡眠时长不足8小时&#xff0c;“失眠”已成为了当代人的生活常态。 越是睡不好&#xff0c;越要想办法。年轻人纷纷求助于好的寝具、好的助眠产品乃至保健品&…

【Halcon】找到设备上的 标识牌

如图&#xff0c;找到设备上的 标识牌 。 标识牌最明显的特征是比其他区域亮&#xff0c; 二值化选择出亮区域&#xff0c;再通过面积选择出目标区域。 先显示图片 *获取图片的大小 get_image_size(Image,Width,Height)*关闭窗口 dev_close_window()*打开窗口 dev_open_win…

java错题总结(19-21页)

链接&#xff1a;关于Java中的ClassLoader下面的哪些描述是错误的_用友笔试题_牛客网 来源&#xff1a;牛客网 B&#xff1a;先讲一下双亲委派机制&#xff0c;简单来说&#xff0c;就是加载一个类的时候&#xff0c;会往上找他的父类加载器&#xff0c;父类加载器找它的父类加…

Centos系统安装RabbitMQ消息中间件

记录一下在centos7.x下面安装RabbitMQ消息中间件 RabbitMQ是一个开源而且遵循 AMQP协议实现的基于 Erlang语言编写&#xff0c;因此安装RabbitMQ之前是需要部署安装Erlang环境的 先安装Erlang https://packagecloud.io/rabbitmq/ 点进去可以看到 因为使用的centos是7.x版本的…
最新文章