Java 手写异步调用
本文最后更新于 931 天前,其中的信息可能已经有所发展或是发生改变。

前言

今天在写 mirai 机器人的一个小功能时,遇到了这样一个需求:机器人需要先发出一条消息,然后间隔 3 秒钟撤回这条消息

当然mirai本身提供了现成的方法,支持异步调用

最朴素的想法是使用Thread.sleep(3000)

public class Test {
    public void A() {
        System.out.println("A");
        try {
            Thread.sleep(3000);
            System.out.println("3S after");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("B");
    }
    public static void main(String[] args) {
        Test test = new Test();
        test.A();
    }
}
//输出:
//A
//3S after
//B

但是这种方法是将方法A的调用线程(即主线程)休眠3S,如果我在主线程中多次调用这个方法,则下一次调用需要等待前一次休眠结束。这就叫阻塞。

主线程:sout(A)->等待3S->sout(B)->sout(A)->等待3S->sout(B)

显然,如果后续有其他的逻辑需要执行,那么这种方式显然不符合需求。

public static void main(String[] args) {
    Test test = new Test();
    test.A();
    test.A();
}
//输出:
//A
//3s after
//B
//A
//3s after
//B
//期望输出:
//A
//A
//3s after
//B
//3s after
//B

创建线程

主线程:sout(A)->创建线程1->sout(2)->创建线程2

线程1:等待3S->sout(B)

线程2:等待3S->sout(B)

public class Test {
    public void A() {
        System.out.println("A");
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("B");
            }
        });
        thread.start();
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.A();
        test.A();
    }
}
//输出:
//A
//A
//3s after
//B
//3s after
//B

这种方法的缺点:

  • 频繁地创建和销毁线程会占用大量的时间
  • 创建线程后,无法跟踪线程的后续完成情况

Executor框架

Executor框架 CSDN

Future 或 FutureTask

public class Test {
    public void A() throws ExecutionException, InterruptedException {
        System.out.println("A");
        //阿里巴巴规范手册不建议用 Executors 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        Future<String> future = executorService.submit(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("3S after");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B");
            return "task B completed";
        });
        //future.get()实际是阻塞的
        //System.out.println(future.get());
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test test = new Test();
        test.A();
        test.A();
    }
}

//输出:
//A
//A
//3s after
//B
//3s after
//B

这种方法的缺点

  • 不同的 Future 之间的关系很难进行关联
  • Future 的 get()方法实际上是阻塞的,直到子线程执行完毕。如果把上面代码中的注释删掉,则输出结果变为:
  A
  3S after
  B
  task B completed
  A
  3S after
  B
  task B completed

CompletableFuture

public class Test {
    public void A() throws ExecutionException, InterruptedException {
        System.out.println("A");
        //阿里巴巴规范手册不建议用 Executors 创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("3S after");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B");
            return "task B completed";
        }, executorService);
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Test test = new Test();
        test.A();
        test.A();
    }
}

//输出:
//A
//A
//3s after
//B
//3s after
//B
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇