谈一谈常见的线程计数器与CompletableFuture

#1024程序员节| 征文#
在这里插入图片描述

CountDownLatch(做减法)

定义:线程同步工具类,允许一个或多个线程一直等待,直到其他线程执行完再被唤醒。在多线程环境下,它指定一个计数器的初始值target,然后可被多个线程并发的实现减 1 操作,并在计数器为 0 后调用 await 方法的线程被唤醒,从而实现多线程间的协作。

应用场景:CountDownLatch非常适合于对任务进行拆分,使其并行执行,比如某个任务执行2s,其对数据的请求可以分为五个部分,那么就可以将这个任务拆分为5个子任务,分别交由五个线程执行,执行完成之后再由主线程进行汇总,此时,总的执行时间将决定于执行最慢的任务,平均来看,还是大大减少了总的执行时间。

主要方法:

CountDownLatch countDownLatch = new CountDownLatch(6);
countDownLatch.countDown(); //计数器减1
countDownLatch.await(); //调用线程会被阻塞,当计数器为0时,才会被唤醒

    static CountDownLatch c = new CountDownLatch(3);
    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(1);
                c.countDown(); // -1
                System.out.println(2);
                c.countDown(); // -1
            }

        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(3);
                c.countDown(); // -1
            }

        }).start();
        c.await(); // 一直等到计算器为0
        System.out.println("over");
    }

CyclicBarrier(做加法)

定义:字面意思是可循环使用的屏障,让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门。初始值0,计数器target再执行(做加法)

应用场景:当主线程进行执行时,利用构造方法初始化一个屏障数和剩余屏障数,子线程调用await方法进行阻塞自身,待到当前组剩余屏障数为0时则唤醒当前组所有被阻塞的线程,然后再执行子线程中的剩余逻辑。

主要方法:

public static void main(String[] args) {
    CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
    for (int i = 0; i < 10; i++){
        int num = i;
        new Thread(()->{
            System.out.println("线程"+num+"初始化!");
            try {
                cyclicBarrier.await();	//先来的线程,被阻塞
                System.out.println("------------------线程"+num+"被唤醒执行!-----------------");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }).start();
    }
    System.out.println("main 线程执行!");
}

Semaphore

定义:多个线程争抢多个共享资源。主要用于两个目的,一是用于多个共享资源的互斥使用,另一个是用于控制并发线程数目(争车位)

应用场景:Semaphore的基本使用场景是限制一定数量的线程能够去执行.

举个简单的例子: 一个单向隧道能同时容纳10个小汽车或5个卡车通过(1个卡车等效与2个小汽车), 而隧道入口记录着当前已经在隧道内的汽车等效比重. 比如1个小汽车和1个卡车, 则隧道入口显示3. 若隧道入口显示10表示已经满了. 当汽车驶出隧道之后, 隧道入口显示的数字则会相应的减小. 于这个示例相符合场景非常适合用信号量.

常用方法:

public static void main(String[] args) {
    Semaphore sem = new Semaphore(2);//共享资源是2个许可
    for (int i = 0; i < 3; i++) {
        new Thread(() -> {
            try {
                sem.acquire();// 默认使用一个许可.
                System.out.println(Thread.currentThread() + " I get it.");
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread() + " I release it.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                sem.release();//释放资源
            }
        }).start();
    }
}

CompletableFuture

定义:CompletableFuture是jdk8的新特性。CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步会点、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利。

创建异步任务

  /**
     * 一、创建异步任务.
     * 带返回值异步请求,默认线程池:       public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
     * 带返回值的异步请求,自定义线程池:    public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
     * 不带返回值的异步请求,默认线程池 :    public static CompletableFuture<Void> runAsync(Runnable runnable)
     * 不带返回值的异步请求,自定义线程池:   public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
     * 二、获取任务结果的方法
     * public T get()                               如果完成则返回结果,否则就抛出具体的异常
     * public T get(long timeout, TimeUnit unit)    最大时间等待返回结果,否则就抛出具体异常
     * public T getNow(T valueIfAbsent)             如果完成则返回结果值(或抛出任何遇到的异常),否则返回给定的 valueIfAbsent
     * public boolean complete(T value)             如果任务没有完成,返回的值设置为给定值
     * public boolean completeExceptionally(Throwable ex)    如果任务没有完成,就抛出给定异常
     * public T join()                              完成时返回结果值,否则抛出unchecked异常
     */
    public static void test001() throws Exception {
        // 1、带返回值异步请求,默认线程池
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("1-带返回值异步请求,默认线程池 is running...");
            return "supplyAsync001";
        });

        // 2、带返回值的异步请求,自定义线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("2-带返回值异步请求,自定义线程池 is running...");
            return "supplyAsync002";
        }, threadPool);

        // 3、不带返回值的异步请求,默认线程池
        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            System.out.println("3-不带返回值的异步请求,默认线程池 is running...");
        });

        // 4、不带返回值的异步请求,自定义线程池
        CompletableFuture<Void> future4 = CompletableFuture.runAsync(() -> {
            System.out.println("4-不带返回值的异步请求,自定义线程池 is running...");
        });

        // 5、获取任务结果的方法
        System.out.println("future1.isDone() = " + future1.isDone());
        System.out.println("future1.get() = " + future1.get());                                         // 若任务没有完成,会阻塞一直等待到任务完成获得结果,后续代码继续执行
        System.out.println("future1.join() = " + future1.join());                                       // 若任务没有完成,会阻塞一直等待到任务完成获得结果,后续代码继续执行
        System.out.println("future1.getNow(\"default\") = " + future1.getNow("default"));    // 若任务没有完成,则返回给定的 valueIfAbsent
        System.out.println("future1.complete(\"supplyAsync001\") = " + future1.complete("supplyAsync001111"));
        System.out.println("future1.get(1, TimeUnit.SECONDS) = " + future1.get(1, TimeUnit.SECONDS)); // 若任务没有完成,则会抛异常,后续逻辑代码不会执行了:java.util.concurrent.TimeoutException
    }

异步回调处理

 /**
     * 三、异步回调处理
     * <p>
     * 1、thenApply和thenApplyAsync:  表示某个任务执行完成后执行的动作,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值【有入参、有返回值】
     * 2、thenAccept和thenAcceptAsync:表示某个任务执行完成后执行的动作,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,无返回值【有入参、无返回值】
     * 3、thenRun和thenRunAsync:      表示某个任务执行完成后执行的动作【无入参,无返回值】
     * 4、whenComplete和whenCompleteAsync:当某个任务执行完成后执行的回调方法,会将执行结果或者执行期间抛出的异常传递给回调方法,如果是正常执行则异常为null,回调方法对应的CompletableFuture的result和该任务一致,如果该任务正常执行,则get方法返回执行结果,如果是执行异常,则get方法抛出异常。
     * 5、handle和handleAsync:跟whenComplete基本一致,区别在于handle的回调方法有返回值。
     */
    public static void test002() throws Exception {
        // 1、thenApply和thenApplyAsync:有入参、有返回值
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future1 do something....");
            return 100;
        }).thenApplyAsync(result -> {
            // 将result = 100 作为入参,执行回调方法
            System.out.println("thenApplyAsync do something....");
            return result + 200;
        });
        System.out.println("future1.get() = " + future1.get());

        // 2、thenAccept和thenAcceptAsync:有入参、无返回值
        CompletableFuture<Void> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future2 do something....");
            return 100;
        }).thenAcceptAsync(result -> {
            // 将result = 100 作为入参,执行回调方法
            System.out.println("thenAcceptAsync do something....");
            System.out.println("result * 3 = " + result * 3);
        });
        System.out.println("future2.get() = " + future2.get());

        // 3、thenRun和thenRunAsync:无入参、无返回值
        CompletableFuture<Void> future3 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future3 do something....");
            return 100;
        }).thenRunAsync(() -> {
            System.out.println("thenRunAsync");
        });
        System.out.println("future3.get() = " + future3.get());

        // 4、whenComplete和whenCompleteAsync:有入参、无返回值
        CompletableFuture<Integer> future4 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future4 do something....");
            return 100;
        }).whenCompleteAsync((result, ex) -> {
            System.out.println("result = " + result);
            System.out.println("ex = " + ex);
        });
        System.out.println("future4.get() = " + future4.get());

        // 5、handle和handleAsync:有入参、有返回值
        CompletableFuture<Integer> future5 = CompletableFuture.supplyAsync(() -> {
            System.out.println("future5 do something....");
            return 1;
        }).handle((result, e) -> {
            System.out.println("handle do something....");
            System.out.println("上个任务结果:" + result);
            System.out.println("上个任务抛出异常:" + e);
            return result + 2;
        });
        System.out.println("future5.get() = " + future5.get());
    }

多任务组合处理之一

 /**
     * 四、多任务组合处理之一
     * allOf:CompletableFuture是多个任务都执行完成后才会执行,只有有一个任务执行异常,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回null。
     * anyOf :CompletableFuture是多个任务只要有一个任务执行完成,则返回的CompletableFuture执行get方法时会抛出异常,如果都是正常执行,则get返回执行完成任务的结果。
     */
    public static void test003() throws Exception {
        // 1、自定义三个任务
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf1任务完成");
            return "cf1任务完成";
        });
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf2任务完成");
            return "cf2任务完成";
        });
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("cf3任务完成");
            return "cf3任务完成";
        });

        // 2、allOf 所有任务都执行完成,才执行thenApply【todo 肯定获取结果后还可以做点业务逻辑】
        CompletableFuture<Void> allOfRes = CompletableFuture.allOf(future1, future2, future3);
        System.out.println("allOfRes.get() = " + allOfRes.get());

        // 3、anyOf 任意一个任务完成,就执行thenApply
        CompletableFuture<Object> anyOfRes = CompletableFuture.anyOf(future1, future2, future3);
        System.out.println("anyOfRes.get() = " + anyOfRes.get());
    }

多任务组合处理之二

/**
     * 四、多任务组合处理之二
     * thenCombine、thenAcceptBoth 和runAfterBoth:
     * 相同点: 这三个方法都是将两个CompletableFuture组合起来处理,只有两个任务都正常完成时,才进行下阶段任务。
     * 区别:
     * thenCombine:将两个任务的执行结果作为所提供函数的参数,且该方法有返回值【有入参、有返回值】;
     * thenAcceptBoth:将两个任务的执行结果作为方法入参,但是无返回值;【有入参、无返回值】
     * runAfterBoth:两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。【无入参、无返回值】
     */
    public static void test004() throws Exception {
        // 自定义两个任务
        CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf1 do something....");
            return 1;
        });

        CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread() + " cf2 do something....");
            return 2;
        });

        // thenCombine:有入参、有返回值
        CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2, (a, b) -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
            return a + b;
        });
        System.out.println("cf3结果->" + cf3.get());

        // thenAcceptBoth:有入参、无返回值
        CompletableFuture<Void> cf4 = cf1.thenAcceptBoth(cf2, (a, b) -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
            System.out.println(a + b);
        });
        System.out.println("cf4.get() = " + cf4.get());

        // runAfterBoth:无入参、无返回值
        CompletableFuture<Void> cf5 = cf1.runAfterBoth(cf2, () -> {
            System.out.println(Thread.currentThread() + " cf3 do something....");
        });
        System.out.println("cf5.get() = " + cf5.get());
    }

多任务组合处理之三

 /**
     * 四、多任务组合处理之三
     * applyToEither、acceptEither和runAfterEither
     * 相同点:这三个方法和上面一样也是将两个CompletableFuture组合起来处理,当有一个任务正常完成时,就会进行下阶段任务。
     * 区别:
     * applyToEither会将已经完成任务的执行结果作为所提供函数的参数,且该方法有返回值;【有入参、有返回值】;
     * acceptEither同样将已经完成任务的执行结果作为方法入参,但是无返回值;【有入参、无返回值】
     * runAfterEither没有入参,也没有返回值。【无入参、无返回值】
     */
    public static void test005() throws Exception {
        // 自定义两个任务
        CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf1 do something....");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf1 任务完成";
        });

        CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
            try {
                System.out.println(Thread.currentThread() + " cf2 do something....");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "cf2 任务完成";
        });
        // applyToEither:有入参、有返回值
        CompletableFuture<String> cf3 = cf1.applyToEither(cf2, (result) -> {
            System.out.println("接收到" + result);
            System.out.println(Thread.currentThread() + " cf3 do something....");
            return "cf3 任务完成";
        });
        System.out.println("cf3结果->" + cf3.get());

        // acceptEither:有入参、无返回值
        CompletableFuture<Void> cf4 = cf1.acceptEither(cf2, (result) -> {
            System.out.println("接收到" + result);
            System.out.println(Thread.currentThread() + " cf4 do something....");
        });
        System.out.println("cf4结果->" + cf4.get());

        // runAfterEither:无入参、无返回值
        CompletableFuture<Void> cf5 = cf1.runAfterEither(cf2, () -> {
            System.out.println(Thread.currentThread() + " cf5 do something....");
            System.out.println("cf5 任务完成");
        });
        System.out.println("cf5结果->" + cf3.get());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员码小跳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值