同步、异步、阻塞、非阻塞的讲解(结合代码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、概念说明
  • 二、代码说明
    • 1、同步阻塞:
    • 2、同步非阻塞:
    • 3、异步非阻塞:
      • 3.1回调函数
      • 3.2 exceptionally方法补充
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

这篇文章将结合代码对同步阻塞、同步非阻塞、异步非阻塞进行说明,至于异步阻塞的情况不存在的。同时也补充说明了异步非阻塞中的回调函数是什么意思。


提示:以下是本篇文章正文内容,下面案例可供参考

一、概念说明

同步:子线程执行完任务之后不会主动通知主线程任务执行结果
异步:子线程执行完任务之后会主动通知主线程任务执行结果
阻塞:主线程需要等待任务执行完之后才能接着往下执行
非阻塞:主线程不需要等待任务执行完之后就能接着往下执行

二、代码说明

1、同步阻塞:

 public static void main(String[] args) throws InterruptedException {
        // 同步阻塞:主线程得等小弟干完活才能继续干活,且小弟干完活不会告诉你,需要主线程自己查看
        FutureTask<String> task = new FutureTask<>(() -> {
            Thread.sleep(5000);
            return "小弟干活了,老main";
        });
        Thread thread = new Thread(task);
        thread.start();
        //task.get()阻塞了主线程,必须得等到子线程执行完后才能继续执行
        try {
            String taskResult = task.get();
            System.out.println("taskResult = " + taskResult);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        //需要主线程主动查看任务执行情况,这里肯定干完活了,因为task.get()阻塞了主线程。主要突出的是主线程需要主动查看子线程的任务执行情况
        System.out.println("小弟你干活没:"+task.isDone());

    }

结果展示:
必须等到子线程执行完任务之后,主线程才能继续往下执行,这就是阻塞。主线程需要主动查看子线程任务执行情况,这叫同步。所以这就是同步阻塞。
同步、异步、阻塞、非阻塞的讲解(结合代码)_第1张图片

2、同步非阻塞:

public static void main(String[] args) throws InterruptedException {
        // 同步非阻塞:主线程在执行任务时不会被阻塞,但是子线程干完活不会告诉你,所以需要你时不时的看一下(isDone进行查看)
        FutureTask<String> task = new FutureTask<>(() -> {
            Thread.sleep(5000);
            return "小弟干活了,老main";
        });
        Thread thread = new Thread(task);
        //子线程开始执行任务
        thread.start();
        //主线程不会被阻塞,继续往下执行
        System.out.println("main  不用干活真的爽");
        Thread.sleep(2000);
        //需要主线程主动查看任务执行情况
        System.out.println("小弟你干活没:"+task.isDone());
        System.out.println("main  不用干活真的爽");
        Thread.sleep(3000);
        System.out.println("main  不用干活真的爽");
        System.out.println("小弟你干活没:"+task.isDone());
        System.out.println("main  不用干活真的爽");
        Thread.sleep(2000);
        System.out.println("main  不用干活真的爽");
        System.out.println("小弟你干活没:"+task.isDone());
    }

结果展示:
主线程不需要等待子线程执行完任务就可以继续往下执行,这叫非阻塞。但是主线程需要主动查看子线程任务执行状态这叫同步,所以这就是同步非阻塞。
同步、异步、阻塞、非阻塞的讲解(结合代码)_第2张图片

3、异步非阻塞:

异步非阻塞,需要大家提前了解一波CompletableFuture。同时在进行串行化流水线式的工作时(任务2需要依赖任务1的处理结果)并不需要用到异步非阻塞!!!
有关于CompletableFuture的内容可以参考这篇链接:
CompletableFuture方法详解(可以把代码copy到本地跑一下,千万不要偷懒哦!)

public static void main(String[] args) throws ExecutionException, InterruptedException {
       ExecutorService executorService = Executors.newSingleThreadExecutor();

       CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
           System.out.println("小弟开始干活啦");
           try {
               Thread.sleep(2000);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
           return "小弟完成!!!";
       },executorService);
       cf.thenAccept(result ->{
           System.out.println("捷报::"+result);
       });
   	//主线程可以继续往下执行
       System.out.println("main线程不用干活就是爽");
       Thread.sleep(1000);
       System.out.println("main线程不用干活就是爽");
       Thread.sleep(1000);
       System.out.println("main线程不用干活就是爽");
       // 关闭线程池
       executorService.shutdown();
   }

结果展示:
主线程让子线程干活,自己可以接下去执行,这就是非阻塞。主线程不需要主动询问子线程是否执行完毕,只需要调用thenAccept回调函数,子线程在执行完之后就会主动告诉主线程任务执行完毕,这就是异步。所以这就是异步非阻塞。
同步、异步、阻塞、非阻塞的讲解(结合代码)_第3张图片


补充:介绍一下回调函数
在这个例子中,定义了一个接口 Callback,其中包含一个回调方法 onComplete,然后创建了一个模拟异步操作的类 AsyncTask,在异步操作完成后调用回调函数:

3.1回调函数

// 回调接口
interface Callback {
    void onComplete(String result);
}

// 模拟异步操作的类
class AsyncTask {
    // 异步操作方法,接收回调对象作为参数
    void doAsyncTask(Callback callback) {
        // 模拟异步操作,比如从数据库或网络获取数据
        new Thread(() -> {
            try {
                // 模拟耗时操作
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 异步操作完成后调用回调方法
            String result = "异步操作的结果";
            callback.onComplete(result);
        }).start();
    }
}

public class CallbackExample {
    public static void main(String[] args) throws InterruptedException {
        // 创建异步任务对象
        AsyncTask asyncTask = new AsyncTask();

        // 调用异步任务,并传入回调函数
        asyncTask.doAsyncTask(new Callback() {
            @Override
            public void onComplete(String result) {
                // 异步操作完成后执行回调函数
                System.out.println("异步操作完成,结果为:" + result);
            }
        });

        System.out.println("主线程继续执行其他操作1");
        Thread.sleep(1000);
        System.out.println("主线程继续执行其他操作2");
        Thread.sleep(1000);
        System.out.println("主线程继续执行其他操作3");
        Thread.sleep(1000);
        System.out.println("主线程继续执行其他操作4");
    }
}

结果展示:
同步、异步、阻塞、非阻塞的讲解(结合代码)_第4张图片
在这个例子中,AsyncTask 类的 doAsyncTask 方法接收一个 Callback 接口的实例作为参数,并在异步操作完成后调用 onComplete 方法。在 main 方法中,我们创建了 AsyncTask 对象,调用了异步方法,并传递了一个匿名类作为回调函数。主线程继续执行其他操作,而异步操作完成后会调用回调函数,打印异步操作的结果。

3.2 exceptionally方法补充

在我们获取异步任务处理结果时不妨试试将get方法换成join方法,你会惊奇的发现join的方法不飘红(即不需要你各种try-catch)。那我们在哪处理异常呢?可以采用这个方法:exceptionally(Function)。只有当前这个异常处理方法,可以获取到前面任务的异常信息,有异常才执行当前任务。
有异常的情况:

    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("task1 is running");
            return 1;
        }).thenApply(result -> {
            System.out.println("task2 is running");
            // 异常在这!!!!!!
            int i = 1/0;
            return result + 2;
        }).exceptionally(throwable -> {
            System.out.println(throwable);
            return 666;
        });
        System.out.println("future.get() = " + future.join());
    }

结果展示:
同步、异步、阻塞、非阻塞的讲解(结合代码)_第5张图片
当发生异常后,exceptionally方法会执行并打印出异常信息,返回一个值(这个值的类型得和异步任务的返回值一致,我这里时Integer)。future.join()方法获取的就是exceptionally中返回的值啦。


无异常情况:

public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println("task1 is running");
            return 1;
        }).thenApply(result -> {
            System.out.println("task2 is running");
            //int i = 1/0;
            return result + 2;
        }).exceptionally(throwable -> {
            System.out.println(throwable);
            return 666;
        });
        System.out.println("future.get() = " + future.join());
    }

结果展示:
同步、异步、阻塞、非阻塞的讲解(结合代码)_第6张图片
没有异常的时候exceptionally方法就不执行啦,future.join()方法获取的就是future正常执行完任务后的返回值啦。

总结

以上就是对同步阻塞、同步非阻塞、异步非阻塞以及回调函数等概念结合代码进行说明,希望大家有所收获,要是有错的地方,恳请大家及时指正!!!

你可能感兴趣的:(java)