Java高阶私房菜:快速学会异步编程CompletableFuture

        为了使主程代码不受阻塞之苦,一般使用异步编程,而异步编程架构在JDK1.5便已有了雏形,主要通过Future和Callable实现,但其操作方法十分繁琐,想要异步获取结果,通常要以轮询的方式去获取结果,具体如下:

  public static void testFuture1() throws Exception {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //定义一个异步任务
        Future<String> future = executorService.submit(()->{
            Thread.sleep(3000);
            return "Future异步请求方法";
        });

        //轮询获取结果,耗费的CPU资源
        while (true){
            if(future.isDone()) {
                System.out.println(future.get());
                break;
            }
        }
    }

        在JDK8后首次引入的CompletableFuture,简化异步编程复杂性,提供了函数式编程让代码更加简洁,可以在任务完成后做对应的callback回调处理。接下来,我带你一步步了解并掌握CompletableFuture。

什么是CompletableFuture

        在项目开发中,由于业务规划逻辑的原因,业务需要从多个不同的地方获取数据,然后汇总处理为最终的结果,再返回给请求的调用方,就是聚合信息处理类的处理逻辑。如果常用串行请求,则接口响应时间长;那么利用CompletableFuture则可以大大提升性能。针对多任务,需要进行任务编排调度,也可以使用CompletableFuture进行完成。
        其内部就是实现了Future和CompletionStage接口,相当于一个Task编排工具。
        Future表示了其异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成。计算完成后只能使用 get 方法来获取结果,有cancel、get、isDone、isCancelled等方法。                

        CompletionStage是Java8新增接口,用于异步执行中的阶段处理,CompletableFuture就是其中的一个实现类。负责对任务处理可以构造一条结果传递链,在结果传递过程中任何一个CompletionStage都可以对结果进行处理,包括异常处理、类型转换,可以构造非常简单的传递链也可以构造很复杂的传递链,几个CompletionStage可以串联起来,一个完成的阶段可以触发下一阶段的执行。
        当前的Task到底由那个Thread执行,使用的不好可能会有性能问题, 根据CompletableFuture的方法命名可以掌握。

        xxxx():表示该方法将继续在当前执行CompletableFuture的方法线程中执行;
        xxxxAsync():表示异步,在线程池中执行。在没有指定线程池的情况下,使用的是CompletableFuture内部的线程池 ForkJoinPool ,线程数默认是 CPU 的核心数。一般不要所有业务共用一个线程池,避免有任务执行一些很慢的 I/O 操作,会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,影响整个系统的性能。

方法API

        CompletableFuture静态方法,执行异步任务的API

//无返回值,默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码
public static CompletableFuture<Void>   runAsync(Runnable runnable)

//无返回值,可以自定义线程池
public static CompletableFuture<Void>  runAsync(Runnable runnable, Executor executor)


//有返回值,默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码
public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier)

//有返回值,可以自定义线程池
public static <U> CompletableFuture<U>  supplyAsync(Supplier<U> supplier, Executor executor)

        CompletableFuture对象,获取结果的API

//如果返回值没有返回,一直阻塞
V get()

//设置等待超时的时间
V get(long timeout,Timeout unit);

//有返回值就返回, 线程抛出异常就返回设置的默认值
T getNow(T defaultValue);

        CompletableFuture对象,其他重点API

//方法无返回值,当前任务正常完成以后执行,当前任务的执行结果可以作为下一任务的输入参数
thenAccept

//方法有返回值,当前任务正常完成以后执行,当前任务的执行的结果会作为下一任务的输入参数
thenApply

//对不关心上一步的计算结果,执行下一个操作
thenRun

异步编程具体代码


public class CompletableFutureDemo {
    public static void main(String[] args) throws Exception {

        testFuture3();

        System.out.println("主线程操作其他----");
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("主线程执行完成");
    }


    //简单案例
    public static void testFuture2() throws ExecutionException, InterruptedException {

        //有返回值,默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) { }
            return "supplyAsync";
        });
        System.out.println("future1返回值:" + future1.get()); //输出 supplyAsync
    }



    //任务编排案例,有返回值
    public static void testFuture3() throws ExecutionException, InterruptedException, TimeoutException {

        //有返回值,默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) { }
            System.out.println("执行任务一");
            return "supplyAsync";
        });
        System.out.println("future1返回值:" + future1.get()); //输出 supplyAsync

        //有返回值,当前任务正常完成以后执行,当前任务的执行的结果会作为下一任务的输入参数
        CompletableFuture<String> future2 = future1.thenApply((element) -> {
            System.out.println("入参:"+element);
            System.out.println("执行任务二");
            try {
                TimeUnit.SECONDS.sleep(6);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "thenApply";
        });

        System.out.println("future2返回值:" + future2.get(1, TimeUnit.SECONDS));

    }



    //任务编排案例,无返回值
    public static void testFuture4() throws ExecutionException, InterruptedException, TimeoutException {

        //有返回值,默认使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() ->{
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) { }
            System.out.println("执行任务一");
            return "supplyAsync";
        });

        //无返回值,当前任务正常完成以后执行,当前任务的执行结果可以作为下一任务的输入参数
        CompletableFuture<Void> future2 = future1.thenAccept((element) -> {
            System.out.println("入参:"+element);
            System.out.println("执行任务二");
        });

        //System.out.println("future2返回值:" + future2.get(1, TimeUnit.SECONDS));
        System.out.println("future2返回值:" + future2.get());

    }

}

CompletableFuture嵌套案例

        日常的任务中,通常定义的方法都会返回 CompletableFuture 类型,方便后续操作,然后将该任务的执行结果Future作为方法入参然后执行指定的方法, 返回一个新的CompletableFuture任务它们之间存在着业务逻辑上的先后顺序。thenCompose用来连接两个CompletableFuture,是生成一个新的CompletableFuture,用于组合多个CompletableFuture,也可以使用 thenApply() 方法来描述关系,但返回的结果就会发生 CompletableFuture 的嵌套,CompletableFuture<CompletableFuture< Product >>  这样的情况,需要get两次

具体代码

public class Product {

    private int id;

    private String title;

    private String detail;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", detail='" + detail + '\'' +
                '}';
    }
}

public class ProductDetailService {

    private static final Map<Integer,String> map = new HashMap<>();
    static {
        map.put(1,"java-详情");
        map.put(2,"python-详情");
        map.put(3,"c#-详情");
        map.put(4,"spring-详情");
        map.put(5,"springboot-详情");
        map.put(6,"harbor-详情");
        map.put(7,"mybatis-详情");
    }

    public String getById(int id){
        try {
            Thread.sleep(1000);
            System.out.println("DetailService # getById方法运行线程:"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return map.get(id);
    }

}


public class ProductService {

    private static final Map<Integer,String> map = new HashMap<>();
    static {
        map.put(1,"java");
        map.put(2,"python");
        map.put(3,"c#");
        map.put(4,"spring");
        map.put(5,"springboot");
        map.put(6,"harbor");
        map.put(7,"mybatis");
    }

    public String getById(int id){
        try {
            Thread.sleep(1000);
            System.out.println("ProductService # getById方法运行线程:"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return map.get(id);
    }

}

public class Test1 {
    public static void main(String[] args) throws Exception {
        testComposeFuture();
    }
    //之前的方案, 方法的返回值也是CompletableFuture,则会出现嵌套
    public static void testEmbedFuture() throws Exception {

        ProductService productService = new ProductService();
        ProductDetailService detailService = new ProductDetailService();

        int id = 1;
        CompletableFuture<CompletableFuture<Product>> future = CompletableFuture.supplyAsync(() -> {
            String title = productService.getById(id);
            Product product = new Product();
            product.setTitle(title);
            product.setId(id);
            System.out.println("步骤1:主线程:" + Thread.currentThread().getName()+",获取产品信息:"+ product);
            return product;
        }).thenApply(new Function<Product, CompletableFuture<Product>>() {
            @Override
            public CompletableFuture<Product> apply(Product product) {
                return CompletableFuture.supplyAsync(() -> {
                    //用到了上个线程的返回值
                    String detail = detailService.getById(product.getId());
                    product.setDetail(detail);
                    System.out.println("步骤2:主线程:" + Thread.currentThread().getName()+",获取产品信息:"+ product);
                    return product;
                });
            }
        });
        System.out.println("线程:" + Thread.currentThread().getName() + " 结果:" + future.get().get().toString());
    }


    //现在的方案
    public static void testComposeFuture() throws Exception {

        ProductService productService = new ProductService();
        ProductDetailService detailService = new ProductDetailService();

        int id = 1;
        CompletableFuture<Product> future = CompletableFuture.supplyAsync(() -> {
                    String title = productService.getById(id);
                    Product product = new Product();
                    product.setTitle(title);
                    product.setId(id);
                    System.out.println("步骤1:主线程:" + Thread.currentThread().getName()+",获取产品信息:"+ product);
                    return product;
                })
                .thenCompose(product -> CompletableFuture.supplyAsync(() -> {
                    String detail = detailService.getById(product.getId());
                    product.setDetail(detail);
                    System.out.println("步骤2:主线程:" + Thread.currentThread().getName()+",获取产品信息:"+ product);
                    return product;

                }));

        System.out.println("线程:" + Thread.currentThread().getName() +
                " 结果:" + future.get().toString());
    }

}

thenCombine合并CompletableFuture案例

        需要请求两个个接口,然后把对应的CompletableFuture进行合并,返回一个新的CompletableFuture

具体代码

public static void testFuture6() throws Exception {

        ProductService productService = new ProductService();
        ProductDetailService detailService = new ProductDetailService();

        int id = 1;
        //第1个任务
        CompletableFuture<Product> baseProductFuture = CompletableFuture.supplyAsync(() -> {
            String title = productService.getById(id);
            Product product = new Product();
            product.setTitle(title);
            product.setId(id);
            return product;
        });

        //第2个任务
        CompletableFuture<Product> detailProductFuture = CompletableFuture.supplyAsync(() -> {
            String detail = detailService.getById(id);
            Product product = new Product();
            product.setDetail(detail);
            product.setId(id);
            return product;
        });


        //将上面2个任务的返回结果baseProduct和detailProduct合并,返回新的包括全部的
        CompletableFuture<Product> resultFuture = baseProductFuture
                .thenCombine(detailProductFuture,
                        new BiFunction<Product, Product, Product>() {
                            @Override
                            public Product apply(Product base, Product detail) {
                                base.setDetail(detail.getDetail());
                                return base;
                            }
                        }
                );

        System.out.println("线程:" + Thread.currentThread().getName() +
                " 结果:" + resultFuture.get().toString());

    }

多个CompletableFuture任务组合调度

        前面学习处理两个 Future 的关系,如果超过两个Future,如何处理他们的一些聚合关系呢?
 方法 allOf  和 anyOf两个函数都是静态函数,参数是变长的 CompletableFuture 的集合,前者是「与」,后者是「或」。
        allOf 返回值是 CompletableFuture< Void >类型,因为allOf没有返回值,所以通过thenApply,获取每个 CompletableFuture 的执行结果。
        anyOf 只要有任意一个 CompletableFuture 结束,就可以做接下来的事情,不像 allOf 要等待所有的 CompletableFuture 结束,每个 CompletableFuture 的返回值类型都可能不同,无法判断是什么类型, 所以 anyOf 的返回值是 CompletableFuture< Object >类型。

具体代码

public class Test2 {
    public static void testAllOf() throws Exception {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future1完成");
            return "future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future2完成");
            return "future2";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future3完成");
            return "future3";
        });
        CompletableFuture<Void> all = CompletableFuture.allOf(future1, future2, future3);
        //阻塞,直到所有任务结束。
        System.out.println(Thread.currentThread().getName() + ":" + LocalDateTime.now() + ":阻塞");
        //调用join方法等待全部任务完成
        all.join();
        if (all.isDone()) {
            //一个需要耗时2秒,一个需要耗时3秒,只有当最长的耗时3秒的完成后,才会结束。
            System.out.println("全部任务完成");
        }
        System.out.println(Thread.currentThread().getName() + ":" + LocalDateTime.now() + ":阻塞结束");
    }

    public static void testAnyOf() throws Exception {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future1完成");
            return "future1";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future2完成");
            return "future2";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("future3完成");
            return "future3";
        });
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
        //阻塞,直到所有任务结束。
        System.out.println(Thread.currentThread().getName() + ":" + LocalDateTime.now() + ":阻塞");
        //调用join方法等待任务完成
        anyOf.join();
        if (anyOf.isDone()) {
            //一个需要耗时2秒,一个需要耗时3秒,当最短的完成则会结束
            System.out.println("全部任务完成:" + anyOf.get());
        }
        System.out.println(Thread.currentThread().getName() + ":" + LocalDateTime.now() + ":阻塞结束");
    }

}

异步编程CompletableFuture案例实战  

        微服务架构下,接口单一职责,一个页面打开涉及多个模块需要同时调用。由于需要同时建立多个连接,中间会有性能损耗,部分页面需要使用聚合接口,则可以用CompletableFuture聚合多个响应结果一次性返回。该方式可以减少建立连接数量,对于网关和服务端可以处理更多连接。
其缺点也非常明显,如果接口性能差异大,则容易性能好的接口被性能差的拖垮。其次就是需要开发更多接口,数据量大则需要更大的带宽。

具体代码

public class EduService {

    public String getRank()  {

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "rank info";
    }


    public String getCategory()  {

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "Category info";
    }


    public String getBanner()  {

        try {
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "Banner info";
    }

    public String getVideoCard()  {

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "Video Card info";
    }


    public static void main(String[] args) throws Exception {
        System.out.println("开始:"+ LocalDateTime.now());
        Map<String, String> stringStringMap = homePageAggApi();
        System.out.println("结束:"+LocalDateTime.now());

        System.out.println(stringStringMap.toString());

        System.out.println("主线程执行完成");
    }

    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            16,
            32,
            30,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    public static Map<String,String> homePageAggApi() throws Exception {


        Map<String,String> homePageInfo = new HashMap<>();
        //模拟不同的服务调用
        EduService eduService = new EduService();

        CompletableFuture<Void> bannerFuture = CompletableFuture.runAsync(() -> {
            String banner = eduService.getBanner();
            homePageInfo.put("banner",banner);
        }, executor);

        CompletableFuture<Void> categoryFuture = CompletableFuture.runAsync(() -> {
            String category = eduService.getCategory();
            homePageInfo.put("category",category);
        }, executor);


        CompletableFuture<Void> rankFuture = CompletableFuture.runAsync(() -> {
            String rank = eduService.getRank();
            homePageInfo.put("rank",rank);
        }, executor);


        CompletableFuture<Void> videoCardFuture = CompletableFuture.runAsync(() -> {
            String videoCard = eduService.getVideoCard();
            homePageInfo.put("videoCard",videoCard);
        }, executor);


        //join()和get()方法都是阻塞调用它们的线程(通常为主线程)用来获取CompletableFuture异步之后的返回值
        CompletableFuture.allOf(bannerFuture,categoryFuture,rankFuture,videoCardFuture)
                .get();

        return homePageInfo;
    }
}

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

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

相关文章

电脑开不了机?不要慌,三招教你快速解决!

电脑开不了机是我们在日常使用中可能遇到的一个严重问题&#xff0c;它会影响我们的工作和生活。了解如何解决电脑开不了机的问题对于维护电脑正常运行至关重要。本文将介绍三种常见的解决电脑开不了机的方法&#xff0c;帮助您快速恢复电脑的正常使用。 方法1&#xff1a;检查…

Java实现生成中间带图标的二维码

Java实现生成中间带图标的二维码 生成Base64格式的二维码&#xff0c;返回html渲染 package your.package;import com.google.zxing.*; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcod…

flask 应用程序

flask 程序示例 创建 hello.py 文件&#xff1a; # 导入 Flask 模块。Flask 类的一个对象是 wsgi 应用程序。 from flask import Flask# 创建app对象, Flask构造函数将当前模块的名称(__name__)作为参数。 app Flask(__name__)# route() 函数是一个装饰器&#xff0c;它告诉应…

Linux上安装mysql指南

最近入职了新单位&#xff0c;申请到了一台cvm。我这个机子是redhat的linux发行版&#xff0c;就用rpm包安装工具就行。下面把我趟过的一些坑写在这里&#xff0c;希望对您有帮助。 一开始&#xff0c;我自己下载了安装包&#xff0c;装了一个这个社区版的&#xff0c;rpm -qa…

鸿蒙入门05-真机运行“遥遥领先”

如果你有一台真的 "遥遥领先"那么是可以直接在手机上真机运行你的项目的我们也来尝试一下运行 一、手机设置开发者模式 打开手机设置 打开手机设置界面 向下滑动到关于手机位置 快速连续点击版本号位置 下图所示位置快速连续点击 打开 3 - 5 次即可 会提示您已经进…

【R语言】动画图:散点图

绘制成如下的散点图&#xff1a; 如果数据量大&#xff0c;有多个年份&#xff0c;就会生成多张图&#xff0c;例如&#xff1a; 具体代码如下&#xff1a; library(gapminder)#加载 gapminder 包&#xff0c;其中包含了从 1952 年至 2007 年各个国家的 GDP、预期寿命和人口数据…

目标检测——鱼类数据集

一、重要性及意义 生物多样性保护与监测&#xff1a; 鱼类识别是生物多样性保护工作的关键一环。通过准确识别不同种类的鱼类&#xff0c;科学家能够更好地了解它们的分布、种群数量以及栖息地状况&#xff0c;从而制定更为有效的保护措施。鱼类是水域生态系统的重要组成部分…

通过阿里云向量检索 Milvus 版和通义千问快速构建基于专属知识库的问答系统

背景介绍 阿里云向量检索 Milvus 版是一款 Serverless 全托管服务&#xff0c;确保了与开源 Milvus 的完全兼容性&#xff0c;并支持无缝迁移。它在开源版本的基础上增强了可扩展性&#xff0c;能提供大规模 AI 向量数据的相似性检索服务。凭借其开箱即用的特性、灵活的扩展能力…

基于百度文心大模型全面重构,小度正式推出AI原生操作系统DuerOS X

4月16日&#xff0c;以“创造未来”为主题的2024百度Create AI开发者大会在深圳举办。百度集团副总裁、小度科技CEO李莹正式发布了小度新一代操作系统DuerOS X&#xff0c;该操作系统是小度基于百度文心大模型推出的全球首个AI原生操作系统。李莹表示&#xff1a;“作为⽂⼼⼤模…

ChatGPT与Python-GEE融合,遥感云大数据分析、管理与可视化

掌握Earth Engine的实际应用能力&#xff0c;以Python为基础&#xff0c;结合实例讲解平台搭建、影像数据分析、经典应用案例、本地与云端数据管理&#xff0c;以及云端数据论文出版级可视化等技能。 为提高教学质量&#xff0c;将融入ChatGPT 4、Claude Opus、Gemini、文心一…

读《SQL基础教程 第二版 上》的一些总结

1. 数据库语言 DDL: Data Definition Language&#xff0c;数据定义语言&#xff08;库、表的操作&#xff09; DML: Data Manipulation Language&#xff0c; 数据操控语言&#xff08;对表中数据的增删改&#xff09; DQL: Data Query Language&#xff0c;数据库查询语言…

SAP是什么?SAP介绍

一、概述 ​SAP,为“System Applications and Products”的简称,是SAP公司的产品——企业管理解决方案的软件名称。​ SAP含义 第一,SAP是公司名称,即SAP公司(纽交所代码:SAP),它是成立于1972年总部位于德国沃尔多夫市的全球最大的企业管理和协同化电子商务解决方案…

SpringBoot多数据源(一)

SpringBoot多数据源&#xff08;一&#xff09; 1.多数据源使用场景1.1 业务复杂&#xff08;数据量大&#xff09;1.2 读写分离 2.多数据源配置3.应用4.测试 1.多数据源使用场景 1.1 业务复杂&#xff08;数据量大&#xff09; 简单理解就是业务量复杂&#xff0c;将庞大的数…

UML/SysML建模工具更新情况-截至2024年4月(1)5款-Trufun建模平台 v2024

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 工具最新版本&#xff1a;itemis CREATE 5.2.2 更新时间 2024年3月22日 工具简介 原名YAKINDU Statechart Tools。状态机建模工具&#xff0c;支持各种语言的代码生成&#xff0c;提…

1000kW 柴油发电机组测试负载箱的核心功能

随着科技的不断发展&#xff0c;电力系统的稳定性和安全性日益受到重视。柴油发电机组作为一种重要的备用电源设备&#xff0c;其性能和可靠性直接关系到电力系统的稳定运行。为了确保柴油发电机组的性能和可靠性&#xff0c;对其进行定期的检测和维护是必不可少的。 在这个过程…

电磁仿真--基本操作-CST-(1)

目录 1. 开启-备忘 2. 从调用最简单的Dipole天线开始 2.1 查找示例 2.2 运行示例 2.3 进度与消息 2.4 查看结果 2.4.1 Port signals 2.4.2 S-Parameter 2.4.3 Reference Impedance 2.4.4 Balance 2.4.5 Power 2.4.6 Energy 2.4.7 Discrete Ports 2.4.8 2D/3D Res…

Spring Boot后端+Vue前端:打造高效二手车交易系统

作者介绍&#xff1a;✌️大厂全栈码农|毕设实战开发&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 &#x1f345;获取源码联系方式请查看文末&#x1f345; 推荐订阅精彩专栏 &#x1f447;&#x1f3fb; 避免错过下次更新 Springboot项目精选实战案例 更多项目…

Linux服务器硬件及RAID配置

一、服务器硬件 塔式服务器&#xff1a;最初的服务器形态之一&#xff0c;类似于传统的台式电脑&#xff0c;但具有更强的处理能力和稳定性&#xff0c;适合小型企业或部门使用。 机架式服务器&#xff1a;设计为可安装在标准化机架内的模块化单元&#xff0c;可以有效地节省空…

好用的AI绘画工具,5个一键AI自动生成绘画推荐

在数字时代&#xff0c;AI绘画软件开启了人们展现创意的全新篇章。如果你对AI一键生成绘画的感兴趣&#xff0c;那就跟着我一起来了解一下吧&#xff01; 1.爱制作AI 爱制作AI是一款功能强大的人工智能软件&#xff0c;它不仅拥有超强的AI问答能力&#xff0c;还能轻松搞定绘画…

Aigtek功率放大器的使用方法有哪些

功率放大器是一种将小信号放大为大信号的电子设备&#xff0c;广泛应用于无线通信、音频系统、雷达等领域。在使用功率放大器时&#xff0c;需要注意以下几个方面&#xff1a; 电源供应&#xff1a;功率放大器需要提供稳定的电源供应以保证正常工作。通常情况下&#xff0c;功率…