【业务功能篇92】微服务-springcloud-多线程-异步处理-异步编排-CompletableFutrue

news2024/11/16 20:24:48

三、CompletableFutrue

一个商品详情页

  • 展示SKU的基本信息 0.5s
  • 展示SKU的图片信息 0.6s
  • 展示SKU的销售信息 1s
  • spu的销售属性 1s
  • 展示规格参数 1.5s
  • spu详情信息 1s

1.ComplatableFuture介绍

  Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用 isDone方法检查计算是否完成,或者使用 get阻塞住调用线程,直到计算完成返回结果,你也可以使用 cancel方法停止任务的执行。

  虽然 Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

  很多语言,比如Node.js,采用回调的方式实现异步编程。Java的一些框架,比如Netty,自己扩展了Java的 Future接口,提供了 addListener等多个扩展方法;Google guava也提供了通用的扩展Future;Scala也提供了简单易用且功能强大的Future/Promise异步编程模式。

  作为正统的Java类库,是不是应该做点什么,加强一下自身库的功能呢?

  在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

  CompletableFuture类实现了Future接口,所以你还是可以像以前一样通过 get方法阻塞或者轮询的方式获得结果,但是这种方式不推荐使用。

  CompletableFuture和FutureTask同属于Future接口的实现类,都可以获取线程的执行结果。

image.png

2.创建异步对象

CompletableFuture 提供了四个静态方法来创建一个异步操作。

static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

方法分为两类:

  • runAsync 没有返回结果
  • supplyAsync 有返回结果
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5
            ,50
            ,10
            , TimeUnit.SECONDS
            ,new LinkedBlockingQueue<>(100)
            , Executors.defaultThreadFactory()
            ,new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        System.out.println("main -- 线程开始了...");
        // 获取CompletableFuture对象
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("线程开始了...");
            int i = 100/50;
            System.out.println("线程结束了...");
        },executor);
        System.out.println("main -- 线程结束了...");

        System.out.println("------------");
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了...");
            int i = 100 / 50;
            System.out.println("线程结束了...");
            return i;
        }, executor);
        System.out.println("获取的线程的返回结果是:" + future.get() );
    }

3.whenXXX和handle方法

  当CompletableFuture的计算结果完成,或者抛出异常的时候,可以执行特定的Action。主要是下面的方法:

public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action);
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor);

public CompletableFuture<T> exceptionally(Function<Throwable,? extends T> fn);

public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) ;
public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) ;

相关方法的说明:

  • whenComplete 可以获取异步任务的返回值和抛出的异常信息,但是不能修改返回结果
  • execptionlly 当异步任务跑出了异常后会触发的方法,如果没有抛出异常该方法不会执行
  • handle 可以获取异步任务的返回值和抛出的异常信息,而且可以显示的修改返回的结果
/**
 * CompletableFuture的介绍
 */
public class CompletableFutureDemo2 {

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5
            ,50
            ,10
            , TimeUnit.SECONDS
            ,new LinkedBlockingQueue<>(100)
            , Executors.defaultThreadFactory()
            ,new ThreadPoolExecutor.AbortPolicy()
    );

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了...");
            int i = 100 / 5;
            System.out.println("线程结束了...");
            return i;
        }, executor).handle((res,exec)->{
            System.out.println("res = " + res + ":exec="+exec);
            return res * 10;
        });
        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + future.get() );
    }

 /*   public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了...");
            int i = 100 / 5;
            System.out.println("线程结束了...");
            return i;
        }, executor).whenCompleteAsync((res,exec)->{
            System.out.println("res = " + res);
            System.out.println("exec = " + exec);
        }).exceptionally((res)->{ // 在异步任务显示的抛出了异常后才会触发的方法
            System.out.println("res = " + res);
            return 10;
        });
        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + future.get() );
    }*/

/*    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了...");
            int i = 100 / 0;
            System.out.println("线程结束了...");
            return i;
        }, executor).whenCompleteAsync((res,exec)->{
            System.out.println("res = " + res);
            System.out.println("exec = " + exec);
        });
        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + future.get() );
    }*/
}

4.线程串行方法

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。

thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。

thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作

带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
/**
 * CompletableFuture的介绍
 */
public class CompletableFutureDemo3 {

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5
            ,50
            ,10
            , TimeUnit.SECONDS
            ,new LinkedBlockingQueue<>(100)
            , Executors.defaultThreadFactory()
            ,new ThreadPoolExecutor.AbortPolicy()
    );

    /**
     * 线程串行的方法
     * thenRun:在前一个线程执行完成后,开始执行,不会获取前一个线程的返回结果,也不会返回信息
     * thenAccept:在前一个线程执行完成后,开始执行,获取前一个线程的返回结果,不会返回信息
     * thenApply: 在前一个线程执行完成后。开始执行,获取前一个线程的返回结果,同时也会返回信息
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了..." + Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor).thenApply(res -> {
            System.out.println("res = " + res);
            return res * 100;
        });
        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + future.get() );
    }
    /*public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了..." + Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor).thenAcceptAsync(res -> {
            System.out.println(res + ":" + Thread.currentThread().getName());
        }, executor);
        // 可以处理异步任务之后的操作
        //System.out.println("获取的线程的返回结果是:" + future.get() );
    }*/
    /*public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("线程开始了..."+Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("线程结束了..."+Thread.currentThread().getName());
            return i;
        }, executor).thenRunAsync(() -> {
            System.out.println("线程开始了..."+Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("线程结束了..."+Thread.currentThread().getName());
        }, executor);
        // 可以处理异步任务之后的操作
        //System.out.println("获取的线程的返回结果是:" + future.get() );
    }*/

}

5.两个都完成

  上面介绍的相关方法都是串行的执行,接下来看看需要等待两个任务执行完成后才会触发的几个方法

  • thenCombine :可以获取前面两线程的返回结果,本身也有返回结果
  • thenAcceptBoth:可以获取前面两线程的返回结果,本身没有返回结果
  • runAfterBoth:不可以获取前面两线程的返回结果,本身也没有返回结果
/**
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1 线程开始了..." + Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("任务1 线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2 线程开始了..." + Thread.currentThread().getName());
            int i = 100 /10;
            System.out.println("任务2 线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor);

        // runAfterBothAsync 不能获取前面两个线程的返回结果,本身也没有返回结果
        CompletableFuture<Void> voidCompletableFuture = future1.runAfterBothAsync(future2, () -> {
            System.out.println("任务3执行了");
        },executor);

        // thenAcceptBothAsync 可以获取前面两个线程的返回结果,本身没有返回结果
        CompletableFuture<Void> voidCompletableFuture1 = future1.thenAcceptBothAsync(future2, (f1, f2) -> {
            System.out.println("f1 = " + f1);
            System.out.println("f2 = " + f2);
        }, executor);

        // thenCombineAsync: 既可以获取前面两个线程的返回结果,同时也会返回结果给阻塞的线程
        CompletableFuture<String> stringCompletableFuture = future1.thenCombineAsync(future2, (f1, f2) -> {
            return f1 + ":" + f2;
        }, executor);

        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + stringCompletableFuture.get() );
    }

6.两个任务完成一个

  在上面5个基础上我们来看看两个任务只要有一个完成就会触发任务3的情况

  • runAfterEither:不能获取完成的线程的返回结果,自身也没有返回结果
  • acceptEither:可以获取线程的返回结果,自身没有返回结果
  • applyToEither:既可以获取线程的返回结果,自身也有返回结果
/**
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1 线程开始了..." + Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("任务1 线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor);
        CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2 线程开始了..." + Thread.currentThread().getName());
            int i = 100 /10;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2 线程结束了..." + Thread.currentThread().getName());
            return i+"";
        }, executor);
        // runAfterEitherAsync 不能获取前面完成的线程的返回结果,自身也没有返回结果
        future1.runAfterEitherAsync(future2,()->{
            System.out.println("任务3执行了....");
        },executor);

        // acceptEitherAsync 可以获取前面完成的线程的返回结果  自身没有返回结果
        future1.acceptEitherAsync(future2,(res)->{
            System.out.println("res = " + res);
        },executor);

        // applyToEitherAsync 既可以获取完成任务的线程的返回结果  自身也有返回结果
        CompletableFuture<String> stringCompletableFuture = future1.applyToEitherAsync(future2, (res) -> {
            System.out.println("res = " + res);
            return res + "-->OK";
        }, executor);
        // 可以处理异步任务之后的操作
        System.out.println("获取的线程的返回结果是:" + stringCompletableFuture.get() );
    }

7.多任务组合

allOf:等待所有任务完成

anyOf:只要有一个任务完成

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);
/**
     * @param args
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Object> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1 线程开始了..." + Thread.currentThread().getName());
            int i = 100 / 5;
            System.out.println("任务1 线程结束了..." + Thread.currentThread().getName());
            return i;
        }, executor);
        CompletableFuture<Object> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2 线程开始了..." + Thread.currentThread().getName());
            int i = 100 /10;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("任务2 线程结束了..." + Thread.currentThread().getName());
            return i+"";
        }, executor);

        CompletableFuture<Object> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务3 线程开始了..." + Thread.currentThread().getName());
            int i = 100 /10;
            System.out.println("任务3 线程结束了..." + Thread.currentThread().getName());
            return i+"";
        }, executor);

        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2, future3);
        anyOf.get();
        System.out.println("主任务执行完成..." + anyOf.get());

        CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2, future3);
        allOf.get();// 阻塞在这个位置,等待所有的任务执行完成
        System.out.println("主任务执行完成..." + future1.get() + " :" + future2.get() + " :" + future3.get());
    }

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

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

相关文章

WPF工控机textbox获得焦点自动打开软键盘

1.通过nuget安装 osklib.wpf 2.在textbox getFoucs中敲入如下代码即可实现获得焦点弹出软键盘 private void txtPLC_IP_GotFocus(object sender, RoutedEventArgs e){try{// Osklib.OnScreenKeyboard.Close();Osklib.OnScreenKeyboard.Show();}catch (Exception ex){MessageB…

烟草企业物流管理信息系统的分析与设计(论文+源码)_kaic

摘要 在经济高速发展的今天&#xff0c;物流业已经成为支撑国民经济的基础性产业。作为一种新型服务业&#xff0c;物流业集仓储、运输、信息等为一体&#xff0c;发展成为复合型战略性产业。S烟草企业设计的物流管理信息系统利用B/S模式的三层结构&#xff0c;基于JSP技术和J…

13.108.Spark 优化、Spark优化与hive的区别、SparkSQL启动参数调优、四川任务优化实践:执行效率提升50%以上

13.108.Spark 优化 1.1.25.Spark优化与hive的区别 1.1.26.SparkSQL启动参数调优 1.1.27.四川任务优化实践&#xff1a;执行效率提升50%以上 13.108.Spark 优化&#xff1a; 1.1.25.Spark优化与hive的区别 先理解spark与mapreduce的本质区别&#xff0c;算子之间&#xff08;…

什么是架构,架构的本质是什么

不论是开发人员还是架构师&#xff0c;我们都一直在跟软件系统打交道&#xff0c;架构是在工作中出现最频繁的术语之一。那么&#xff0c;到底什么是架构&#xff1f;你可能有自己的答案&#xff0c;也有可能没有答案。对“架构”的理解需要我们不断在实践中思考、归纳、演绎&a…

说说Lambda架构

分析&回答 Lambda架构是由Storm的作者Nathan Marz提出的一个实时大数据处理框架。Marz在Twitter工作期间开发了著名的实时大数据处理框架Storm&#xff0c;Lambda架构是其根据多年进行分布式大数据系统的经验总结提炼而成。Lambda架构的目标是设计出一个能满足实时大数据系…

高教社杯数模竞赛特辑论文篇-2018年C题:基于 RFMT 模型的百货商场会员画像描绘(附获奖论文及代码实现)

目录 赛题 摘要 一、问题的重述 二、模型假设 三、变量说明 四、模型的建立与求解 4.1 数据预处理 4.2 问题一的模型建立与求解 4.2.1 建模思路 4.2.2 模型建立 4.2.3 模型的求解与结果分析 4.3 问题二的模型建立与求解 4.3.1 建模思路 4.3.2 模型建立 4.3.3 模…

污水厂数字孪生 | 3D可视化管理系统助力污水企业数字化管理

随着城市化进程的不断加快&#xff0c;污水处理成为了城市环境保护的重要组成部分。传统的污水处理方式往往存在诸多问题&#xff0c;如信息不对称、安全隐患等。为了解决这些问题&#xff0c;污水处理3D可视化管控平台应运而生&#xff0c;它通过结合数字孪生技术和远程指导技…

详解Python argparse ---命令行选项、参数和子命解析器

详解argparse模块 一、 模块简介二、使用步骤三、ArgumentParser(&#xff09;参数四、add_argument&#xff08;&#xff09;参数详解五、示例 一、 模块简介 argparse模块使编写用户友好的命令行界面变得容易。该程序定义了它需要什么参数&#xff0c;argparse将找出如何从s…

使用C语言计算1/1-1/2+1/3-1/4+...+1/99-1/100

观察算式&#xff0c;发现分子都是1&#xff0c;分母从1~100&#xff0c;所以可以使用for循环产生1~100之间的数。 另一个问题是&#xff0c;如何产生正负交替的符号&#xff1f;很简单&#xff0c;这个符号本质上就是往每一项前面乘一个系数&#xff1a;一或者负一。所以只需…

纽扣电池/锂电池UN38.3安全检测报告

根据规章要求&#xff0c;航空公司和机场货物收运部门应对锂电池进行运输文件审查&#xff0c;重要的是每种型号的锂电池UN38.3安全检测报告。该报告可由的三方检测机构。如不能提供此项检测报告&#xff0c;将禁止锂电池进行航空运输. UN38.3包含产品&#xff1a;1、 锂电池2…

AI建模 | 物体三维重建的高效方法

三维重建是将客观世界中的物体在虚拟空间表达出来&#xff0c;在大众视野中&#xff0c;物品三维重建最直观的应用当属虚拟仿真和VR/AR导航。其实在学科专业领域&#xff0c;三维重建已经更早地应用在高精地图、测绘系统、城市规划等领域。 科技发展的终极方向应当是普适性&am…

dll修复精灵下载方法,完美解决电脑d3dx9-d3dx11dll文件丢失方法

大家好&#xff01;今天&#xff0c;我将为大家带来一场关于d3dx9_43.dll丢失的6种解决方法的演讲。希望通过这次演讲&#xff0c;能够帮助大家解决在电脑使用过程中遇到的问题&#xff0c;提高我们的生活和工作效率。 首先&#xff0c;让我们来了解一下d3dx9_43.dll是什么文件…

《QDebug 2023年8月》

一、Qt Widgets 问题交流 1.获取 QWidget 当前所在屏幕区域 本来以为 QWidget 的 screen() 接口返回的是组件自己所在屏幕的 QSreen&#xff0c;实测是所属 Window 所在的屏幕&#xff0c;如果 Window 跨屏了两者所属屏幕可能就不是同一个。 获取 QWidget 当前所在屏幕区域可…

jmeter单接口和多接口测试

最近接触到了多接口串联&#xff0c;接口串联的技术会在其他帖子有说明&#xff0c;其核心技术点就是通过正则表达式和变量来实现接口的关联。目前为止呢笔者用到的地方还只有一个&#xff0c;就是关于session保持的时候。但是看到很多资料都说测试过程中经常遇到b接口需要用a接…

IDA Pro反汇编工具下载安装使用

一、前言 IDA Pro&#xff08;Interactive Disassembler Professional&#xff09;简称“IDA”&#xff0c;是Hex-Rays公司出品的一款交互式反汇编工具&#xff0c;是目前最棒的一个静态反编译软件&#xff0c;为众多0day世界的成员和ShellCode安全分析人士不可缺少的利器。ID…

R3LIVE项目实战(5) — R3LIVE数据采集与时间同步

目录 1 R3LIVE数据采集运行步骤 1.1 录制数据集 1.2 修改config下对应的配置文件 1.3 启动相机和雷达节点 1.4 运行R3LIVE与播包 2 R3LIVE在线运行 1 R3LIVE数据采集运行步骤 1.1 录制数据集 采集数据需要注意的一点是&#xff0c;不同传感器之间的时间同步问题&#x…

【C语言练习】C语言如何操作内存(重中之重!!!)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

Android 之 GPS 初涉

本节引言&#xff1a; 说到GPS这个名词&#xff0c;相信大家都不陌生&#xff0c;GPS全球定位技术嘛&#xff0c;嗯&#xff0c;Android中定位的方式 一般有这四种&#xff1a;GPS定位&#xff0c;WIFI定准&#xff0c;基站定位&#xff0c;AGPS定位(基站GPS)&#xff1b; 本…

Linux的内存理解

建议 Mysql机器 尽量不要硬swap,如果是ssd磁盘还好。Free命令 free 命令显示系统内存的使用情况,包括物理内存、交换内存(swap)和内核缓冲区内存 输出简介: Mem 行(第二行)是内存的使用情况。Swap 行(第三行)是交换空间的使用情况。total 列显示系统总的可用物理内存和交换…

【高阶产品策略】策略产品数据与行为分析方法

文章目录 1、策略产品数据与用户行为数据分析概述2、埋点、策略数据收集核心技能3、用户行为数据分析应用4、数据平台实施 1、策略产品数据与用户行为数据分析概述 2、埋点、策略数据收集核心技能 3、用户行为数据分析应用 4、数据平台实施