提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:这里可以添加本文要记录的大概内容:
这篇文章将结合代码对同步阻塞、同步非阻塞、异步非阻塞进行说明,至于异步阻塞的情况不存在的。同时也补充说明了异步非阻塞中的回调函数是什么意思。
提示:以下是本篇文章正文内容,下面案例可供参考
同步:子线程执行完任务之后不会主动通知主线程任务执行结果
异步:子线程执行完任务之后会主动通知主线程任务执行结果
阻塞:主线程需要等待任务执行完之后才能接着往下执行
非阻塞:主线程不需要等待任务执行完之后就能接着往下执行
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());
}
结果展示:
必须等到子线程执行完任务之后,主线程才能继续往下执行,这就是阻塞。主线程需要主动查看子线程任务执行情况,这叫同步。所以这就是同步阻塞。
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());
}
结果展示:
主线程不需要等待子线程执行完任务就可以继续往下执行,这叫非阻塞。但是主线程需要主动查看子线程任务执行状态这叫同步,所以这就是同步非阻塞。
异步非阻塞,需要大家提前了解一波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回调函数,子线程在执行完之后就会主动告诉主线程任务执行完毕,这就是异步。所以这就是异步非阻塞。
补充:介绍一下回调函数
在这个例子中,定义了一个接口 Callback
,其中包含一个回调方法 onComplete
,然后创建了一个模拟异步操作的类 AsyncTask
,在异步操作完成后调用回调函数:
// 回调接口
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");
}
}
结果展示:
在这个例子中,AsyncTask
类的 doAsyncTask
方法接收一个 Callback
接口的实例作为参数,并在异步操作完成后调用 onComplete
方法。在 main
方法中,我们创建了 AsyncTask
对象,调用了异步方法,并传递了一个匿名类作为回调函数。主线程继续执行其他操作,而异步操作完成后会调用回调函数,打印异步操作的结果。
在我们获取异步任务处理结果时不妨试试将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());
}
结果展示:
当发生异常后,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());
}
结果展示:
没有异常的时候exceptionally方法就不执行啦,future.join()方法获取的就是future正常执行完任务后的返回值啦。
以上就是对同步阻塞、同步非阻塞、异步非阻塞以及回调函数等概念结合代码进行说明,希望大家有所收获,要是有错的地方,恳请大家及时指正!!!