(十六)异步编程

news2025/1/15 19:40:55

CompletableFuture

在Java8中推出,Java8中的异步编程就是依靠此类。

几种任务接口

四种任务

无参数

有一个参数

有两个参数

无返回值

Runnable

Consumer

BiConsumer

有返回值

Supplier

Function

BiFunction

CompletionStage接口

这个类中定义的许多能够链式调用的方法和组合方法时是CompletableFuture能实现异步编程的关键。所有方法的返回值都是CompletionStage类型,所以能够链式调用,例如以下方法:

以thenApply为例,thenApply接受一个Function。Function的参数是<?Super T >类型,也就是T或者T的父类型,而T是调用thenApply的CompletableFuture对象泛型的类型;返回值是<?Extends U>类型,也就是U或者U的子类型,而U恰好是thenApply的返回的CompletionStage对应的泛型类型。
其他函数也是类似。
public interface CompletionStage<T> {
public <U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn);
public CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenRun(Runnable action);
public <U,V> CompletionStage<V> thenCombine
        (CompletionStage<? extends U> other,
         BiFunction<? super T,? super U,? extends V> fn);
public <U> CompletionStage<Void> thenAcceptBoth
        (CompletionStage<? extends U> other,
         BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<U> thenCompose
        (Function<? super T, ? extends CompletionStage<U>> fn);
//......
}

使用

测试类
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import org.junit.Test;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

public class AsyncTest {
    private void waitRandomTime(int bound) {
        ThreadLocalRandom random = ThreadLocalRandom.current();
        try {
            TimeUnit.SECONDS.sleep(random.nextInt(bound));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void waitRandomTime5() {
        waitRandomTime(5);
    }
}

run

适用于执行没有返回值的任务。
@Test
public void testRun() throws ExecutionException, InterruptedException {
    CompletableFuture<Void> future = CompletableFuture.runAsync(//Runnable
            () -> {
                System.out.println(LocalDateTime.now().toString() + ":1开始跑...");
                waitRandomTime5();
                System.out.println(LocalDateTime.now().toString() + ":1跑完啦...");
            }
    ).thenRunAsync(//Runnable
            () -> {
                System.out.println(LocalDateTime.now().toString() + ":2开始跑...");
                waitRandomTime5();
                System.out.println(LocalDateTime.now().toString() + ":2跑完啦...");
            }
    ).whenComplete(
            (v, t) -> System.out.println("完成啦")
    );
    future.get();
}

supply

适用于执行有返回值的任务,可以把上个任务的返回值传到下个任务,适当组合可以实现reduce的效果。
@Test
public void testSupplier() throws ExecutionException, InterruptedException {
    CompletableFuture<Void> future = CompletableFuture.supplyAsync(//Supplier
            () -> {
                System.out.println(LocalDateTime.now().toString() + ":1开始跑...");
                waitRandomTime5();
                System.out.println(LocalDateTime.now().toString() + ":1跑完啦...");
                return "hello,world";
            }
    ).thenApply(//Function
            (res) -> {
                System.out.println("1的结果:" + res);
                System.out.println(LocalDateTime.now().toString() + ":2开始跑...");
                waitRandomTime5();
                System.out.println(LocalDateTime.now().toString() + ":2跑完啦...");
                return res + "你好,世界";
            }
    ).thenAcceptAsync(//Consumer
            (res)-> {
                System.out.println("1,2合并的结果:" + res);
                System.out.println(LocalDateTime.now().toString() + ":3开始跑...");
                waitRandomTime5();
                System.out.println(LocalDateTime.now().toString() + ":3跑完啦...");
            }
    ).whenComplete(
            (v, t) -> System.out.println("完成啦")
    );
    future.get();
}

compose

用来合并嵌套的CompletableFuture。如果在任务中又返回了CompletableFuture类型,则在最外层的返回值中泛型类型会嵌套,使用compose则可以消除这种嵌套。
@Test
public void testCompose() throws ExecutionException, InterruptedException {
    //不使用compose,返回值嵌套
    CompletableFuture<CompletableFuture<String>> future1 =
            CompletableFuture
                    .supplyAsync(() -> "hello")
                    .thenApplyAsync(
                            (s) -> CompletableFuture.supplyAsync(() -> {
                                waitRandomTime5();
                                return s + ",world";
                            })
                    );
    String s1 = future1.get().get();
    System.out.println(s1);
    //使用compose展开,返回值没有嵌套
    CompletableFuture<String> future2 =
            CompletableFuture
                    .supplyAsync(() -> "hello")
                    .thenComposeAsync(
                            (s) -> CompletableFuture.supplyAsync(() -> {
                                waitRandomTime5();
                                return s + ",world";
                            })
                    );
    String s2 = future2.get();
    System.out.println(s2);
}

combine

用来执行完两个CompletableFuture后再使用自定义的函数执行某些操作。
@Test
public void testCombine() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> num1 = CompletableFuture.supplyAsync(
            () -> {
                waitRandomTime5();
                int n = ThreadLocalRandom.current().nextInt(10);
                System.out.println("第一个数:"+n);
                return n;
            }
    );
    CompletableFuture<Integer> num2 = CompletableFuture.supplyAsync(
            () -> {
                waitRandomTime5();
                int n = ThreadLocalRandom.current().nextInt(10);
                System.out.println("第二个数:"+n);
                return n;
            }
    );
    CompletableFuture<Integer> future = num1.thenCombineAsync(num2, (n1, n2) -> n1 * n2);
    System.out.println("乘积:"+future.get());
}

allOf

可以组合任意多个CompletableFuture,所有的CompletableFuture执行完成才算完成,是“与”的关系。
它的返回值是CompletableFuture<Void>类型,因为有的可能有返回值,有的可能没有返回值,所以用直接返回Void,想获取返回值用get的方式,如以下例子:
下面的例子模拟多个用户ID调用接口返回用户信息,最终在apply中收集结果到list中。
@Test
public void testAllOf() throws ExecutionException, InterruptedException {
    List<String> userIds = new ArrayList<>();
    userIds.add("zhangsan");
    userIds.add("lisi");
    userIds.add("wangwu");
    List<CompletableFuture<User>> userInfos = userIds.stream().map((id) -> getUserInfo(id)).collect(Collectors.toList());
    List<User> users = CompletableFuture
            .allOf(userInfos.toArray(new CompletableFuture[userInfos.size()]))
            .thenApplyAsync(
                    //收集结果
                    (v) -> userInfos.stream().map((future) -> {
                        try {
                            return future.get();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (ExecutionException e) {
                            e.printStackTrace();
                        }
                        return null;
                    }).collect(Collectors.toList())
            ).get();
    users.forEach(System.out::println);
}
private CompletableFuture<User> getUserInfo(String userId) {
    //模拟掉接口,根据用户id返回用户信息
    return CompletableFuture.supplyAsync(
            () -> {
                waitRandomTime5();
                User user = new User();
                user.setId(userId);
                user.setAge(ThreadLocalRandom.current().nextInt(20,30));
                user.setMoney(BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble(50, 100)));
                return user;
            }
    );
}
@Getter
@Setter
@EqualsAndHashCode
static class User {
    private String id;
    private int age;
    private BigDecimal money;

    @Override
    public String toString() {
        return "[id="+id+",age="+age+",money="+money.setScale(2,BigDecimal.ROUND_HALF_UP).toString()+"]";
    }
}

anyOf

也可以组合任意多个CompletableFuture,不过任意一个CompletableFuture执行完成就算完成,其余的结果会被丢弃,是“或”的关系。
返回值类型是CompletableFuture<Object>,因为最终只有一个任务真正执行完成,所以返回值只有一个,用Object可以适配所有返回值类型。
@Test
public void testAnyOf() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() ->
    {
        waitRandomTime5();
        return 1;
    });
    CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->
    {
        waitRandomTime5();
        return 2;
    });
    CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() ->
    {
        waitRandomTime5();
        return 3;
    });
    CompletableFuture<Object> f = CompletableFuture.anyOf(future1, future2, future3);
    System.out.println("最终结果:"+f.get());
}

原理

任务适配

CompletableFuture的执行交给了ForkJoinPool(一般并行度一定大于1,否则forkjoin的价值就体现不出来了)。因为ForkJoinPool只能执行ForkJoinTask类型的任务,这就涉及到任务转换问题,CompletableFuture内部定义了一些适配器接口用来做任务的转换。
private static final boolean useCommonPool =
    (ForkJoinPool.getCommonPoolParallelism() > 1);

private static final Executor asyncPool = useCommonPool ?
    ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
下图为CompletableFuture内部定义的一些适配器类
在supplierAsync(..)函数内部,会把一个Supplier 转换成一个AsyncSupply,然后提交给ForkJoinPool执行;
在runAsync(..)函数内部,会把一个Runnable转换成一个AsyncRun,然后提交给ForkJoinPool执行;
在thenRun/thenAccept/thenApply 内部,会分别把Runnable/Consumer/Function 转换成UniRun/UniAccept/UniApply对象,然后提交给ForkJoinPool执行;
在whenComplete内部,会将BiConsumer转换为UniWhenComplete,然后提交给ForkJoinPool执行;
除此之外,还有两种CompletableFuture 组合的情况(例如combine),分为“与”和“或”,所以有对应的Bi和Or类型的Completion类型。

链式调用

以下列代码为例,分析一下链式调用过程
@Test
public void testLink() throws ExecutionException, InterruptedException {
    CompletableFuture<Void> future = CompletableFuture
            .supplyAsync(() -> "hello")
            .thenApplyAsync((s) -> s + ",world")
            .thenAcceptAsync((s) -> System.out.println(s))
            .thenRunAsync(() -> System.out.println("完成"));
    future.get();
}

Completion

abstract static class Completion extends ForkJoinTask<Void>
    implements Runnable, AsynchronousCompletionTask {
    volatile Completion next;      

    /**
     * 如果触发,则执行 完成操作,返回可能需要传播的依赖(如果存在)
     *
     * @param 模式 同步、异步或嵌套
     */
    abstract CompletableFuture<?> tryFire(int mode);

    abstract boolean isLive();

    public final void run()                { tryFire(ASYNC); }
    public final boolean exec()            { tryFire(ASYNC); return true; }
    public final Void getRawResult()       { return null; }
    public final void setRawResult(Void v) {}
}
Completion是一个Treiber Stack,Treiber Stack是一个无锁的栈,由R.Kent Treiber在其于1986年发表的论文Systems Programming:Coping with Para llelism 中首次提出。它是一个单向链表,出栈、入栈都在链表头部,所以只需要一个head指针,而不需要tail指针。
    • 执行supplyAsync

第1步的关键是构造了一个AsyncSupply对象,该对象有几个关键点:
(1)继承自ForkJoinTask,能够提交给ForkJoinPool来执行。
(2)封装了Supplier f
(3)封装了该任务的返回值,即变量d。
该任务的输入就是Supply,输出结果存放在新建的CompletableFuture的result字段中。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
    return asyncSupplyStage(asyncPool, supplier);
}

static <U> CompletableFuture<U> asyncSupplyStage(Executor e,
                                                 Supplier<U> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<U> d = new CompletableFuture<U>();
    e.execute(new AsyncSupply<U>(d, f));
    return d;
}

static final class AsyncSupply<T> extends ForkJoinTask<Void>
        implements Runnable, AsynchronousCompletionTask {
    CompletableFuture<T> dep; Supplier<T> fn;
    AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) {
        this.dep = dep; this.fn = fn;
    }

    public final Void getRawResult() { return null; }
    public final void setRawResult(Void v) {}
    public final boolean exec() { run(); return true; }

    public void run() {
        CompletableFuture<T> d; Supplier<T> f;
        if ((d = dep) != null && (f = fn) != null) {
            dep = null; fn = null;
            if (d.result == null) {
                try {
                    //结果放在result中
                    d.completeValue(f.get());
                } catch (Throwable ex) {
                    d.completeThrowable(ex);
                }
            }
            d.postComplete();
        }
    }
}
    • 执行thenApplyAsync

第2步必须等第1步执行完成后再执行。此处构建了一个UniApply对象,这个任务放入了第1个任务的栈当中。
每一个CompletableFuture都有一个Treiber Stack(stack字段,栈顶指针),存放着它后续依赖的任务。
在第1步的AsyncSupply类中run方法里调用了CompletableFuture的postComplete方法,此方法里会弹出本步骤压入的UniApply对象执行。
public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(asyncPool, fn);
}

private <V> CompletableFuture<V> uniApplyStage(
    Executor e, Function<? super T,? extends V> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<V> d =  new CompletableFuture<V>();
    if (e != null || !d.uniApply(this, f, null)) {
        UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

final void push(UniCompletion<?,?> c) {
    if (c != null) {
        // 压入Treiber Stack,如果有返回值则不压栈
        while (result == null && !tryPushStack(c))
            lazySetNext(c, null); // 失败进行清理,直到成功为止
    }
}

static final class UniApply<T,V> extends UniCompletion<T,V> {
    Function<? super T,? extends V> fn;
    UniApply(Executor executor, CompletableFuture<V> dep,
             CompletableFuture<T> src,
             Function<? super T,? extends V> fn) {
        super(executor, dep, src); this.fn = fn;
    }
    final CompletableFuture<V> tryFire(int mode) {
        CompletableFuture<V> d; CompletableFuture<T> a;
        if ((d = dep) == null ||
            !d.uniApply(a = src, fn, mode > 0 ? null : this))
            return null;
        dep = null; src = null; fn = null;
        return d.postFire(a, mode);
    }
}

final void postComplete() {
    CompletableFuture<?> f = this; Completion h;
    while ((h = f.stack) != null ||
           (f != this && (h = (f = this).stack) != null)) {
        CompletableFuture<?> d; Completion t;
        //弹出栈
        if (f.casStack(h, t = h.next)) {
            if (t != null) {
                if (f != this) {
                    pushStack(h);
                    continue;
                }
                h.next = null;  
            }
            f = (d = h.tryFire(NESTED)) == null ? this : d;
        }
    }
}
    • 执行thenAcceptAsync

第三步必须等第二步执行完成后再执行。此处构建了一个UniAccept对象,这个任务放入了第2个任务的栈当中。当第2步的任务执行完成时,从栈中弹出UniAccept对象并执行。
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
    return uniAcceptStage(asyncPool, action);
}

private CompletableFuture<Void> uniAcceptStage(Executor e,
                                               Consumer<? super T> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<Void> d = new CompletableFuture<Void>();
    if (e != null || !d.uniAccept(this, f, null)) {
        UniAccept<T> c = new UniAccept<T>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

static final class UniAccept<T> extends UniCompletion<T,Void> {
    Consumer<? super T> fn;
    UniAccept(Executor executor, CompletableFuture<Void> dep,
              CompletableFuture<T> src, Consumer<? super T> fn) {
        super(executor, dep, src); this.fn = fn;
    }
    final CompletableFuture<Void> tryFire(int mode) {
        CompletableFuture<Void> d; CompletableFuture<T> a;
        if ((d = dep) == null ||
            !d.uniAccept(a = src, fn, mode > 0 ? null : this))
            return null;
        dep = null; src = null; fn = null;
        return d.postFire(a, mode);
    }
}
    • 执行thenRunAsync

第4步和第3步的过程类似,构建了一个UniRun 对象,这个对象被压入第3步的CompletableFuture的栈中。第3步的任务执行完成时,从栈中弹出UniRun对象并执行。
public CompletableFuture<Void> thenRunAsync(Runnable action) {
        return uniRunStage(asyncPool, action);
    }
private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
        if (f == null) throw new NullPointerException();
        CompletableFuture<Void> d = new CompletableFuture<Void>();
        if (e != null || !d.uniRun(this, f, null)) {
            UniRun<T> c = new UniRun<T>(e, d, this, f);
            push(c);
            c.tryFire(SYNC);
        }
        return d;
    }
static final class UniRun<T> extends UniCompletion<T,Void> {
        Runnable fn;
        UniRun(Executor executor, CompletableFuture<Void> dep,
               CompletableFuture<T> src, Runnable fn) {
            super(executor, dep, src); this.fn = fn;
        }
        final CompletableFuture<Void> tryFire(int mode) {
            CompletableFuture<Void> d; CompletableFuture<T> a;
            if ((d = dep) == null ||
                !d.uniRun(a = src, fn, mode > 0 ? null : this))
                return null;
            dep = null; src = null; fn = null;
            return d.postFire(a, mode);
        }
    }

不带Async后缀的方法

除了supply,其他方法都有不带Async后缀的同名方法,例如thenApplyAsync和thenApply,他们的区别如下:
public <U> CompletableFuture<U> thenApply(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(null, fn);
}

public <U> CompletableFuture<U> thenApplyAsync(
    Function<? super T,? extends U> fn) {
    return uniApplyStage(asyncPool, fn);
}
看来都是调用的同一个方法,只不过参数不一样,区别在于if (e != null || !d.uniApply(this, f, null))这一句:
thenApplyAsync 判断e!=null固定为true,进入if分支执行;
thenApply会调用d.uniApply(this, f, null),如果前一个任务还没执行完,则也会进入if分支,如果前一个任务执行完了,result有值,则不会入栈,直接返回。
其他类似方法同理。
private <V> CompletableFuture<V> uniApplyStage(
    Executor e, Function<? super T,? extends V> f) {
    if (f == null) throw new NullPointerException();
    CompletableFuture<V> d =  new CompletableFuture<V>();
    if (e != null || !d.uniApply(this, f, null)) {
        UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
        push(c);
        c.tryFire(SYNC);
    }
    return d;
}

final <S> boolean uniApply(CompletableFuture<S> a,
                           Function<? super S,? extends T> f,
                           UniApply<S,T> c) {
    Object r; Throwable x;
    //之前的任务还没执行完
    if (a == null || (r = a.result) == null || f == null)
        return false;
    tryComplete: if (result == null) {
        if (r instanceof AltResult) {
            if ((x = ((AltResult)r).ex) != null) {
                completeThrowable(x, r);
                break tryComplete;
            }
            r = null;
        }
        try {
            if (c != null && !c.claim())
                return false;
            @SuppressWarnings("unchecked") S s = (S) r;
            completeValue(f.apply(s));
        } catch (Throwable ex) {
            completeThrowable(ex);
        }
    }
    return true;
}

网状任务

任务执行时有可能不是单单链表式的调用,还可能形成一个网络结构,用图表示。例如下面的有向无环图:

执行过程

任务1执行完成之后,任务2、任务3可以并行,伪代码:
future1.thenApply(任务2),future1.thenApply(任务3);
任务4在任务2执行完成时可开始执行;
任务5要等待任务2、任务3都执行完成,才能开始,是and关系;
任务6在任务3执行完成时可以开始执行;
对于任务7,只要任务4、5、6中任意一个任务结束,就可以开始执行,是or的关系。

总之,任务依赖之间是多对多的关系:1个任务可以有n个依赖它的后继任务;1个任务也可以有n个它依赖的前驱任务。

(1)在每个任务的返回值里面,存储了依赖它的接下来要执行的任务。任务1的CompletableFuture的栈中存储了任务2、任务3;任务2的CompletableFuutre中存储了任务4、任务5;任务3的CompletableFuture中存储了任务5、任务6。也就是说,每个任务的CompletableFuture对象的栈里面,存储了该节点的 出边对应的任务集合。

(2)任务2、任务3的CompletableFuture里面,都存储了任务5,任务5会被触发2次,但它会判断任务2、任务3的结果是不是都完成,如果只完成其中一个,它就不会执行。

(3)任务7存在于任务4、任务5、任务6的CompletableFuture的栈里面,因此会被触发三次。但它只会执行一次,只要其中1个任务执行完成,就可以执行任务7。

(4)因为有and和or 两种不同的关系,因此对应BiApply和OrApply两个类,这两个对象的构造函数几乎一样,只是在内部执行的时候,一个是and的逻辑,一个是or的逻辑。

(5)BiApply和OrApply都是二元操作符,只能传入二个被依赖的任务。但是任何一个多元操作,都能被转换为多个二元操作的叠加。
上面的任务7同时依赖于任务4、任务5、任务6,就可以采用下图的转换方法,and转换类似。

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

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

相关文章

Unity3DVR开发—— XRInteractionToolkit(PicoNeo3)

目录 一、开发前的准备 二、基础配置 三、Pico项目配置 四、添加基础功能 一、开发前的准备 1、为了方便开发&#xff0c;先在Pico开发者平台里下载预览工具 Pico开发者平台https://developer-global.pico-interactive.com/sdk?deviceId1&platformId1&itemId17 2、…

【哈希表】关于哈希表,你该了解这些!

【哈希表】理论基础1 哈希表2 哈希函数3 哈希碰撞3.1 拉链法3.2 线性探测法4 常见的三种哈希结构5 总结1 哈希表 哈希表 Hash table &#xff08;一些书籍翻译为散列表&#xff09; 哈希表是根据关键码的值而直接进行访问的数据结构。 直白来讲其实数组就是一张哈希表。 哈希表…

用1行Python代码识别增值税发票,YYDS

大家好&#xff0c;这里是程序员晚枫。 录入发票是一件繁琐的工作&#xff0c;如果可以自动识别并且录入系统&#xff0c;那可真是太好了。 今天我们就来学习一下&#xff0c;如何自动识别增值税发票并且录入系统~ 识别发票 识别发票的代码最简单&#xff0c;只需要1行代码…

CSS的总结

从HTML被发明开始&#xff0c;样式就以各种形式存在。不同的浏览器结合它们各自的样式语言为用户提供页面效果的控制。最初的HTML只包含很少的显示属性。 随着HTML的成长&#xff0c;为了满足页面设计者的要求&#xff0c;HTML添加了很多显示功能。但是随着这些功能的增加&…

ISIS的路由器级别level-1、level-2、level-1-2,报文格式

2.1.0 ISIS的路由器级别level-1、level-2、level-1-2&#xff0c;报文格式 通过该文章了解ISIS的路由器级别类型、级别之间建立的邻接关系、各级别的作用、ISIS报文的结构。 ISIS路由器级别 Level-1 level-1路由器又称L1路由器&#xff0c;是一种ISIS区域内部路由&#xff0c…

6、运算符

目录 一、赋值运算符 二、算数运算符 三、自增和自减运算符 四、比较运算符 五、逻辑运算符 六、位运算符 1. “按位与”运算 2. “按位或”运算 3. “按位取反”运算 4. “按位异或”运算 5. 移位操作 七、三元运算符 八、运算符优先级 一、赋值运算符 赋值运算…

[Android开发基础1] 五大常用界面布局

文章目录 一、线性布局 二、相对布局 三、帧布局 四、表格布局 五、约束布局 总结 一、线性布局 线性布局&#xff08;LinearLayout&#xff09;主要以水平或垂直方式来显示界面中的控件。当控件水平排列时&#xff0c;显示顺序依次为从左到右&#xff0c;当控件垂直排列时…

29/365 java 网络通信 IP InetAddress

1.网络通信&#xff1a; 如何定位到一台主机&#xff1f; IP地址 定位主机&#xff0c; 端口号定位到具体的应用程序 如何在主机之间通信&#xff08;传输数据&#xff09;&#xff1f; 网络通信协议 2.IP地址分类 IPv4: 32位 IPv6地址&#xff1a;128位 IPv6地址使用以冒号…

初学python100例-案例37 合并排序列表 少儿编程python编程实例讲解

目录 python合并排序列表 一、题目要求 1、编程实现 2、输入输出

C语言形参和实参的区别

如果把函数比喻成一台机器&#xff0c;那么参数就是原材料&#xff0c;返回值就是最终产品&#xff1b;从一定程度上讲&#xff0c;函数的作用就是根据不同的参数产生不同的返回值。这一节我们先来讲解C语言函数的参数&#xff0c;下一节再讲解C语言函数的返回值。C语言函数的参…

浅谈多任务学习

目录 一、前言及定义 二、多任务学习&#xff08;MTL&#xff09;的两种方法 2.1 参数的硬共享机制&#xff08;hard parameter sharing&#xff09; 2.2 参数的软共享机制&#xff08;soft parameter sharing&#xff09; 三、多任务学习模型 3.1 MT-DNN 3.2 ERNIE 2.0…

数学建模学习笔记(9)多元线性回归分析(非常详细)

多元线性回归分析1.回归分析的地位、任务和分类2.数据的分类3.对线性的理解、系数的解释和内生性4.取对数预处理、虚拟变量和交互效应5.使用Stata进行多元线性回归分析6.异方差7.多重共线性8.逐步回归法1.回归分析的地位、任务和分类 回归分析的地位&#xff1a;数据分析中最基…

cclow 面试心得

开源ccflow学习的一些心得目录概述需求&#xff1a;设计思路实现思路分析1.心得参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge …

JavaEE-文件和IO(一)

目录一、文件1.1 认识文件1.2 树型结构组织和目录1.3 文件路径二、Java中操作文件2.1 文件系统相关的操作一、文件 1.1 认识文件 平时说的文件一般都是指存储再硬盘上的普通文件&#xff0c;形如txt&#xff0c;jpg&#xff0c;MP4&#xff0c;rar等这些文件都可以认为是普通…

Java集合常见面试题(四)

Map 接口 HashMap 的底层实现 JDK1.8 之前 JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列&#xff0c;数组是 HashMap 的主体&#xff0c;链表则是主要为了解决哈希冲突而存在的。 HashMap 通过 key 的 hashcode 经过扰动函数&#xff08;hash函数&…

JAVA基础知识08集合基础

目录 1. 集合 1.1 什么是集合&#xff1f; 1.2 ArrayList 1.2.1 ArrayList 长度可变原理 1.2.2 集合和数组的使用选择 1.2.3 ArrayList 集合常用成员方法 1. 集合 1.1 什么是集合&#xff1f; 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组。 其长度可…

线段树的懒标记与应用

目录 一、前言 二、Lazy-tag技术 1、update() 中的lazy-tag 三、例题 1、区间修改、区间查询&#xff08;lanqiaoOJ 1133&#xff09; 一、前言 本文主要讲了线段树的Lazy-tag技术和一道例题&#xff0c;建议自己要多练习线段树的题目。 二、Lazy-tag技术 背景&#xf…

水面漂浮物垃圾识别检测系统 YOlOv7

水面漂浮物垃圾识别检测系统通过PythonYOLOv7网络模型&#xff0c;实现对水面漂浮物以及生活各种垃圾等全天候24小时不间断智能化检测。Python是一种由Guido van Rossum开发的通用编程语言&#xff0c;它很快就变得非常流行&#xff0c;主要是因为它的简单性和代码可读性。它使…

Linux- 系统随你玩之--文本处理三剑客-带头一哥-awk

文章目录1、awk概述2、awk原理2.1、 awk 工作原理2.2、 与sed工作原理比较2.3、 awk与sed的区别3、使用方法及原理3.1、格式如下&#xff1a;3.2、 匹配规则3.3、 参数说明3.4、处理规则与流程控制3.5、 常用 awk 内置变量3.6、 awk 正则表达式解释4、操作实例4.1、 准备工作4.…

(十七)抽象队列同步器AQS

AQSAbstractQueuedSynchronizer抽象同步队列简称AQS&#xff0c;它是实现同步器的基础组件&#xff0c;并发包中锁的底层就是使用AQS实现。类图如下&#xff0c;AbstractQueuedLongSynchronizer与AbstractQueuedSynchronizer结构一模一样&#xff0c;只是AbstractQueuedSynchro…