并发编程-CompletableFuture解析

2023-10-27

1、CompletableFuture介绍

CompletableFuture对象是JDK1.8版本新引入的类,这个类实现了两个接口,一个是Future接口,一个是CompletionStage接口。

CompletionStage接口是JDK1.8版本提供的接口,用于异步执行中的阶段处理,CompletionStage定义了一组接口用于在一个阶段执行结束之后,要么继续执行下一个阶段,要么对结果进行转换产生新的结果等,一般来说要执行下一个阶段都需要上一个阶段正常完成,这个类也提供了对异常结果的处理接口

2、CompletableFuture的API

2.1 提交任务

在CompletableFuture中提交任务有以下几种方式:

public 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)

这四个方法都是用来提交任务的,不同的是supplyAsync提交的任务有返回值,runAsync提交的任务没有返回值。两个接口都有一个重载的方法,第二个入参为指定的线程池,如果不指定,则默认使用ForkJoinPool.commonPool()线程池。在使用的过程中尽量根据不同的业务来指定不同的线程池,方便对不同线程池进行监控,同时避免业务共用线程池相互影响。

2.2 结果转换

2.2.1 thenApply

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)

thenApply这一组函数入参是Function,意思是将上一个CompletableFuture执行结果作为入参,再次进行转换或者计算,重新返回一个新的值。

2.2.2 handle

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)

handle这一组函数入参是BiFunction,该函数式接口有两个入参一个返回值,意思是处理上一个CompletableFuture的处理结果,同时如果有异常,需要手动处理异常。

2.2.3 thenRun

public CompletableFuture<Void> thenRun(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action)
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor)

thenRun这一组函数入参是Runnable函数式接口,该接口无需入参和出参,这一组函数是在上一个CompletableFuture任务执行完成后,在执行另外一个接口,不需要上一个任务的结果,也不需要返回值,只需要在上一个任务执行完成后执行即可。

2.2.4 thenAccept

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

thenAccept这一组函数的入参是Consumer,该函数式接口有一个入参,没有返回值,所以这一组接口的意思是处理上一个CompletableFuture的处理结果,但是不返回结果。

2.2.5 thenAcceptBoth

public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action)
public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor)

thenAcceptBoth这一组函数入参包括CompletionStage以及BiConsumer,CompletionStage是JDK1.8新增的接口,在JDK中只有一个实现类:CompletableFuture,所以第一个入参就是CompletableFuture,这一组函数是用来接受两个CompletableFuture的返回值,并将其组合到一起。BiConsumer这个函数式接口有两个入参,并且没有返回值,BiConsumer的第一个入参就是调用方CompletableFuture的执行结果,第二个入参就是thenAcceptBoth接口入参的CompletableFuture的执行结果。所以这一组函数意思是将两个CompletableFuture执行结果合并到一起。

2.2.6 thenCombine

public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor)

thenCombine这一组函数和thenAcceptBoth类似,入参都包含一个CompletionStage,也就是CompletableFuture对象,意思也是组合两个CompletableFuture的执行结果,不同的是thenCombine的第二个入参为BiFunction,该函数式接口有两个入参,同时有一个返回值。所以与thenAcceptBoth不同的是,thenCombine将两个任务结果合并后会返回一个全新的值作为出参。

2.2.7 thenCompose

public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor)

thenCompose这一组函数意思是将调用方的执行结果作为Function函数的入参,同时返回一个新的CompletableFuture对象。

2.3 回调方法

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)

whenComplete方法意思是当上一个CompletableFuture对象任务执行完成后执行该方法。BiConsumer函数式接口有两个入参没有返回值,这两个入参第一个是CompletableFuture任务的执行结果,第二个是异常信息。表示处理上一个任务的结果,如果有异常,则需要手动处理异常,与handle方法的区别在于,handle方法的BiFunction是有返回值的,而BiConsumer是没有返回值的。

以上方法都有一个带有Async的方法,带有Async的方法表示是异步执行的,会将该任务放到线程池中执行,同时该方法会有一个重载的方法,最后一个参数为Executor,表示异步执行可以指定线程池执行。为了方便进行控制,最好在使用CompletableFuture时手动指定我们的线程池。

2.4 异常处理

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

exceptionally是用来处理异常的,当任务抛出异常后,可以通过exceptionally来进行处理,也可以选择使用handle来进行处理,不过两者有些不同,hand是用来处理上一个任务的结果,如果有异常情况,就处理异常。而exceptionally可以放在CompletableFuture处理的最后,作为兜底逻辑来处理未知异常。

2.5 获取结果

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

allOf是需要入参中所有的CompletableFuture任务执行完成,才会进行下一步;

anyOf是入参中任何一个CompletableFuture任务执行完成都可以执行下一步。

public T get() throws InterruptedException, ExecutionException
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
public T getNow(T valueIfAbsent)
public T join()

get方法一个是不带超时时间的,一个是带有超时时间的。

getNow方法则是立即返回结果,如果还没有结果,则返回默认值,也就是该方法的入参。

join方法是不带超时时间的等待任务完成。

3、CompletableFuture原理

join方法同样表示获取结果,但是join与get方法有什么区别呢。

public T join() {
    Object r;
    return reportJoin((r = result) == null ? waitingGet(false) : r);
}

public T get() throws InterruptedException, ExecutionException {
    Object r;
    return reportGet((r = result) == null ? waitingGet(true) : r);
}

public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        Object r;
        long nanos = unit.toNanos(timeout);
        return reportGet((r = result) == null ? timedGet(nanos) : r);
}

public T getNow(T valueIfAbsent) {
        Object r;
        return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
}

以上是CompletableFuture类中两个方法的代码,可以看到两个方法几乎一样。区别在于reportJoin/reportGet,waitingGet方法是一致的,只不过参数不一样,我们在看下reportGet与reportJoin方法。

private static <T> T reportGet(Object r)
        throws InterruptedException, ExecutionException {
        if (r == null) // by convention below, null means interrupted
            throw new InterruptedException();
        if (r instanceof AltResult) {
            Throwable x, cause;
            if ((x = ((AltResult)r).ex) == null)
                return null;
            if (x instanceof CancellationException)
                throw (CancellationException)x;
            if ((x instanceof CompletionException) &&
                (cause = x.getCause()) != null)
                x = cause;
            throw new ExecutionException(x);
        }
        @SuppressWarnings("unchecked") T t = (T) r;
        return t;
    }

private static <T> T reportJoin(Object r) {
        if (r instanceof AltResult) {
            Throwable x;
            if ((x = ((AltResult)r).ex) == null)
                return null;
            if (x instanceof CancellationException)
                throw (CancellationException)x;
            if (x instanceof CompletionException)
                throw (CompletionException)x;
            throw new CompletionException(x);
        }
        @SuppressWarnings("unchecked") T t = (T) r;
        return t;
    }

可以看到这两个方法很相近,reportGet方法判断了r对象是否为空,并抛出了中断异常,而reportJoin方法没有判断,同时reportJoin抛出的都是运行时异常,所以join方法也是无需手动捕获异常的。

我们在看下waitingGet方法

private Object waitingGet(boolean interruptible) {
        Signaller q = null;
        boolean queued = false;
        int spins = -1;
        Object r;
        while ((r = result) == null) {
            if (spins < 0)
                spins = SPINS;
            else if (spins > 0) {
                if (ThreadLocalRandom.nextSecondarySeed() >= 0)
                    --spins;
            }
            else if (q == null)
                q = new Signaller(interruptible, 0L, 0L);
            else if (!queued)
                queued = tryPushStack(q);
            else if (interruptible && q.interruptControl < 0) {
                q.thread = null;
                cleanStack();
                return null;
            }
            else if (q.thread != null && result == null) {
                try {
                    ForkJoinPool.managedBlock(q);
                } catch (InterruptedException ie) {
                    q.interruptControl = -1;
                }
            }
        }
        if (q != null) {
            q.thread = null;
            if (q.interruptControl < 0) {
                if (interruptible)
                    r = null; // report interruption
                else
                    Thread.currentThread().interrupt();
            }
        }
        postComplete();
        return r;
    }

该waitingGet方法是通过while的方式循环判断是否任务已经完成并产生结果,如果结果为空,则会一直在这里循环,这里需要注意的是在这里初始化了一下spins=-1,当第一次进入while循环的时候,spins是-1,这时会将spins赋值为一个常量,该常量为SPINS。

private static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
                                      1 << 8 : 0);

这里判断可用CPU数是否大于1,如果大于1,则该常量为 1<< 8,也就是256,否则该常量为0。

第二次进入while循环的时候,spins是256大于0,这里做了减一的操作,下次进入while循环,如果还没有结果,依然是大于0继续做减一的操作,此处用来做短时间的自旋等待结果,只有当spins等于0,后续会进入正常流程判断。

我们在看下timedGet方法的源码

private Object timedGet(long nanos) throws TimeoutException {
        if (Thread.interrupted())
            return null;
        if (nanos <= 0L)
            throw new TimeoutException();
        long d = System.nanoTime() + nanos;
        Signaller q = new Signaller(true, nanos, d == 0L ? 1L : d); // avoid 0
        boolean queued = false;
        Object r;
        // We intentionally don't spin here (as waitingGet does) because
        // the call to nanoTime() above acts much like a spin.
        while ((r = result) == null) {
            if (!queued)
                queued = tryPushStack(q);
            else if (q.interruptControl < 0 || q.nanos <= 0L) {
                q.thread = null;
                cleanStack();
                if (q.interruptControl < 0)
                    return null;
                throw new TimeoutException();
            }
            else if (q.thread != null && result == null) {
                try {
                    ForkJoinPool.managedBlock(q);
                } catch (InterruptedException ie) {
                    q.interruptControl = -1;
                }
            }
        }
        if (q.interruptControl < 0)
            r = null;
        q.thread = null;
        postComplete();
        return r;
    }

timedGet方法依然是通过while循环的方式来判断是否已经完成,timedGet方法入参为一个纳秒值,并通过该值计算出一个deadline截止时间,当while循环还未获取到任务结果且已经达到截止时间,则抛出一个TimeoutException异常。

4、CompletableFuture实现多线程任务

这里我们通过CompletableFuture来实现一个多线程处理异步任务的例子。

这里我们创建10个任务提交到我们指定的线程池中执行,并等待这10个任务全部执行完毕。

每个任务的执行流程为第一次先执行加法,第二次执行乘法,如果发生异常则返回默认值,当10个任务执行完成后依次打印每个任务的结果。

public void demo() throws InterruptedException, ExecutionException, TimeoutException {
        // 1、自定义线程池
        ExecutorService executorService = new ThreadPoolExecutor(5, 10,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100));

        // 2、集合保存future对象
        List<CompletableFuture<Integer>> futures = new ArrayList<>(10);
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            CompletableFuture<Integer> future = CompletableFuture
                    // 提交任务到指定线程池
                    .supplyAsync(() -> this.addValue(finalI), executorService)
                    // 第一个任务执行结果在此处进行处理
                    .thenApplyAsync(k -> this.plusValue(finalI, k), executorService)
                    // 任务执行异常时处理异常并返回默认值
                    .exceptionally(e -> this.defaultValue(finalI, e));
            // future对象添加到集合中
            futures.add(future);
        }

        // 3、等待所有任务执行完成,此处最好加超时时间
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(5, TimeUnit.MINUTES);
        for (CompletableFuture<Integer> future : futures) {
            Integer num = future.get();
            System.out.println("任务执行结果为:" + num);
        }
        System.out.println("任务全部执行完成!");
    }

    private Integer addValue(Integer index) {
        System.out.println("第" + index + "个任务第一次执行");
        if (index == 3) {
            int value = index / 0;
        }
        return index + 3;
    }

    private Integer plusValue(Integer index, Integer num) {
        System.out.println("第" + index + "个任务第二次执行,上次执行结果:" + num);
        return num * 10;
    }

    private Integer defaultValue(Integer index, Throwable e) {
        System.out.println("第" + index + "个任务执行异常!" + e.getMessage());
        e.printStackTrace();
        return 10;
    }

作者:京东物流 丁冬

来源:京东云开发者社区 自猿其说Tech

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并发编程-CompletableFuture解析 的相关文章

  • 我可以在 Java 枚举上使用构建器模式吗

    我正在重写一些代码 并且我已经决定了重新创建类的方法 因为有固定数量的工作表 我将它们创建为枚举 这是基于构建器模式与伸缩构造器的可读性的决定 我的代码获取一些 xls 文件 添加标题 并从其他 xls 文件中读取一些 也许还有一些子表 然
  • 相当于 java PBKDF2WithHmacSHA1 的 Python

    我的任务是构建一个 API 的使用者 该 API 需要带有 UNIX 时间种子值的加密令牌 我看到的示例是使用我不熟悉的 Java 实现的 在阅读文档和其他堆栈文章后一直无法找到解决方案 使用javax crypto SecretKey j
  • Google App Engine 数据存储写入:如何远程启用/禁用只读模式?

    在阅读备份时GAE 的数据存储 https developers google com appengine docs adminconsole datastoreadmin where 我们强烈建议您在备份或恢复期间将应用程序设置为只读模式
  • 从 Eclipse 导出后,WAR 文件中缺少一些必要的库 - 为什么?

    我接手了一个大学的项目 其中包含一些 Web 服务 通过将项目导出为 WAR 文件 一些库包含在文件中 例如 Axis2 而另一些则不包含 hibernate JDBC 驱动程序 另外 添加到类路径中的 jar 尚未导出 所有库都位于硬盘驱
  • java中%%是什么意思?

    我是一名 PHP 程序员 想知道这行代码的含义 System out printf exp 3f is 3f n x Math exp x 3f 3f n 和逗号 x 是什么意思 它与C类似printf http java sun com
  • 如何在android中使用retrofit访问404错误?

    我正在使用改造 2 访问 REST API 以使用原始正文插入 JSON 数据 我从服务器获得成功响应 但在响应时收到 404 错误 我想访问404错误请帮我解决这个问题 ApiUtil getServiceClass sendFinalC
  • Java:等于和==

    让我们看看我们有 2 个对用户定义类实例的引用 即 Java 中的 a 和 b 会不会有一种情况 a b 但 a equals b 返回 false 当然 实施 equals 完全取决于班级 所以我可以写 class Foo public
  • 在 Spring Webflux 中执行阻塞 JDBC 调用

    我使用 Spring Webflux 和 Spring data jpa 使用 PostgreSql 作为后端数据库 我不想在进行数据库调用时阻塞主线程 例如find and save 为了实现同样的目标 我有一个主调度程序Controll
  • 使用 SSL 和代理设置的 Rest 客户端获取连接超时

    我正在使用带有忽略 ssl 的 Rest 客户端 它工作正常 但在将来我尝试使用客户端证书进行的生产中将无法工作 我有 ca 证书和客户端证书 我用它创建了一个客户端 但我收到错误 Exception in thread main com
  • Android Studio:如果设置项目的背景颜色,ListView OnClick 动画将不起作用

    在我的项目中 我在 ListView 内设置了项目 由插入 ConstraintLayout 中的多个元素组成 的背景颜色 但如果背景颜色不是至少一点透明 则单击和长按的默认动画会消失 事实上 随着透明度的降低 点击元素的效果越来越不明显
  • 使用 Microsoft REST API - Java 将 Xbox-Live GamerTag 转换为 XUID

    我有一个 Java 应用程序 它需要能够获取用户输入的 Minecraft Bedrock Edition 玩家标签 并将其转换为给定帐户的 XUID 以便我可以将其存储起来以供稍后列入白名单和参考目的 我一直在浏览 Microsoft R
  • python 中的子进程调用以使用 JAVA_OPTS 调用 java jar 文件

    示例代码 import subprocess subprocess call java jar temp jar 如何在上面的命令中指定JAVA OPTS 当我使用上述命令时 我收到 java lang OutOfMemoryError 无
  • Map:为 Integer 和 Double 类型定义方法,但不为 String 类型定义方法

    我正在尝试定义一个方法putIfGreaterThan 为了我的新Map class 给定一个键 仅当新值大于旧值时 它才会用新值替换旧值 我知道我可以通过组合来实现这一点 通过有一个private final Map
  • Java:从 ScriptEngine javascript 返回一个对象

    我正在尝试使用 Java 来评估 javascript脚本引擎 https docs oracle com javase 7 docs api javax script ScriptEngine html班级 这是我正在尝试做的事情的一个简
  • 无法删除临时文件夹(有时)

    当我启动应用程序时 我创建一个临时文件夹 public static File createTempDir String name throws IOException File tempDir File createTempFile na
  • Java:如果数组大小未知,如何初始化?

    我要求用户输入 1 到 100 之间的一些数字并将它们分配到一个数组中 数组大小未初始化 因为它取决于用户输入数字的次数 我应该如何分配数组长度 如果用户输入 5 6 7 8 9 5 个数字 则 int list becomes int l
  • @Transactional 注解属于哪里?

    如果您将 Transactional in the DAO类和 或其方法 或者注释使用 DAO 对象调用的服务类是否更好 或者注释两个 层 是否有意义 我认为事务属于服务层 它是了解工作单元和用例的人 如果您将多个 DAO 注入到需要在单个
  • 从侦听器中修改 JFrame [重复]

    这个问题在这里已经有答案了 可能的重复 如何在框架可见后调用 setUndecorated https stackoverflow com questions 875132 how to call setundecorated after
  • 为什么 CompletableFuture 的 thenAccept() 不在主线程上运行

    我在 CompletableFuture 的 SupplyAsync 中处理长时间运行的操作 并将结果放入 thenAccept 中 有时 thenAccept 在主线程上执行 但有时它在工作线程上运行 但我只想在主线程上运行 thenAc
  • 图标和导航视图之间的左边距

    我必须在图标和图标之间添加左边距NavigationView 如下图中箭头所示 我知道根据谷歌规范 这个边距必须有16dp但我需要改变它 我努力了

随机推荐

  • 科普篇

    开发者是开源社区生生不息的源动力 为了汇聚开发者力量 共建开源生态 FISCO BCOS智能合约编译技术专项兴趣小组 Compiling Technology of Smart Contract Special Interest Group
  • 链游

    一 前言 身为一名从事移动互联网App开发多年的从业人员 一直在关注前沿的技术方向 以免被时代的列车遗落 从18年底开始关注DApp技术的发展 COCOS发起的COCOS BCX以区块链为底层技术 专注区块链游戏领域的DApp游戏开发框架自
  • 【半监督学习】4、Dense Teacher

    文章目录 一 背景 二 方法 2 1 框架结构 2 2 伪标签的缺陷 2 3 Dense Peseudo Label 三 效果 3 1 数据和实验设置 3 2 主要的结果对比 3 3 和 SOTA 的对比 论文 Dense Teacher
  • yarn upgrade 更新依赖包时yarn.lock更新但package.json不同步更新版本信息

    背景 独立维护项目的架构与开发工作 在升级依赖包时发现使用yarn upgrade更新依赖包 yarn lock文件更新了 但是package json里依赖包的版本没有更新 解决方案 下载npm check updates yarn up
  • 完美解决Python各种no module named "XX"问题

    在腾讯云上玩Django 但总是遇到no module name django core wsgi 等问题 在django的 error log中也提示是 no module 但是 本地 python3 wsgi py或者 python3
  • 被 GitHub 「临时邮箱」项目拉黑,Firefox Relay 引热议;业内首个开源容器安全平台发布;Deepin 20.4 发布

    整理 宋彤彤 责编 屠敏 开源吞噬世界的趋势下 借助开源软件 基于开源协议 任何人都可以得到项目的源代码 加以学习 修改 甚至是重新分发 关注 开源日报 一文速览国内外今日的开源大事件吧 一分钟速览新闻点 开源大新闻 SUSE 发布业内首个
  • IE8兼容

    X UA Compatible是针对ie8新加的一个设置 对于ie8之外的浏览器是不识别的 这个区别与 content IE 7 在无论页面是否包含指令 都像是使用了 Windows Internet Explorer 7的标准模式 而co
  • 大话赛宁云

    如今 随着数字时代的飞速发展 安全漏洞存在于网络空间中 对系统造成极大的安全隐患 为网络攻击者的恶意入侵提供了捷径 对此 解决这一困境 要秉承 快速 自动 安全 的解决标准 首先需要高技术手段的支持 实施常态化演练 及时发现安全漏洞 测评危
  • 【计算机视觉

    文章目录 一 检测相关 8篇 1 1 Attending Generalizability in Course of Deep Fake Detection by Exploring Multi task Learning 1 2 Harv
  • 【华为OD统一考试B卷

    在线OJ 已购买本专栏用户 请私信博主开通账号 在线刷题 运行出现 Runtime Error 0Aborted 请忽略 华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一
  • MVC控制器(Controller)与策略模式(Strategy)

    MVC控制器 Controller 与策略模式 Strategy 转载时请注明出处和作者联系方式 http blog csdn net absurd 作者联系方式 Li XianJing
  • Hashpump实现哈希长度扩展攻击

    Hashpump实现哈希长度扩展攻击 RCEME 0x01 HASH长度拓展攻击 哈希长度拓展攻击的原理有点过于复杂了 这里直接copy其他大佬的描述了 长度扩展攻击 length extension attack 是指针对某些允许包含额外
  • C#

    System IO Pipelines 很酷的读写数据流方式 文章目录 System IO Pipelines 很酷的读写数据流方式 前言 System IO Pipelines 是啥 有什么优点 有哪些应用场景 网络编程 文件处理 怎么使
  • "防止同时出现多个应用程序实例"之改进

    防止同时出现多个应用程序实例 之改进字号 大 中 小 在 Delphi 5 开发人员指南 中第13章中有一篇 防止同时出现多个应用程序实例 代码中给出了一个MultInst pas单元 工程引用此单元就能防止同时出现多个实例 但实际应用中发
  • Linux中find命令-path -prune用法详解(find命令如何只查找当前目录,而不查找当前目录下的子目录find . ! -name "." -type d -prune -o -type f -name "*.txt" -print)

    在Windows中可以在某些路径中查找文件 也可以设定不在某些路径中查找文件 下面用Linux中的find的命令结合其 path prune参数来看看在Linux中怎么实现此功能 假如在当前目录下查找文件 且当前目录下有很多文件及目录 多层
  • python自动化赚钱-薅羊毛

    1 目 标 场 景 最近 有一个朋友告诉我 她在某平台上购买了一部手机 收到货之后发现商品质量挺好的 价格也不贵 临了随手给了个好评 商家最后还给她发一个 小红包 她把这个商品分享给了我 本篇文章的目的是利用Python 自动化完成商品购买
  • iOS16新特性:实时活动-在锁屏界面实时更新APP消息

    简介 之前在 iOS16新特性 灵动岛适配开发与到家业务场景结合的探索实践 里介绍了iOS16新的特性 实时更新 Live Activity 中灵动岛的适配流程 但其实除了灵动岛的展示样式 Live Activity还有一种非常实用的应用场
  • 王者荣耀助手动态服务器维护中,王者荣耀助手动态怎么发不了

    发布时间 2015 11 20 王者荣耀助手礼包怎么领取 助手礼包领取地址是什么 下面来看看王者荣耀助手礼包怎么领取 助手礼包领取地址一览吧 希望能对大家有所帮助 蚕豆网王者荣耀开黑群 439442380 点击加入 亲爱的召唤师 安卓版 l
  • MySQL之InnoDB存储结构

    1 InnoDB存储引擎 InnoDB存储引擎最早由Innobase Oy公司开发 属第三方存储引擎 从MySQL 5 5版本开始作为表的默认存储引擎 该存储引擎是第一个完整支持ACID事务的MySQL存储引擎 特点是行锁设计 支持MVCC
  • 并发编程-CompletableFuture解析

    1 CompletableFuture介绍 CompletableFuture对象是JDK1 8版本新引入的类 这个类实现了两个接口 一个是Future接口 一个是CompletionStage接口 CompletionStage接口是JD