这几个概念困扰了我好久,现在此做下总结:
阻塞:阻塞通常是指在一个执行过程中暂停,以等待某个条件的触发。(包括IO 阻塞、线程阻塞)
同步/异步:表示的发出调用后,被调用者是否立即返回。
阻塞/非阻塞:表示调用方线程是否在等待数据就绪(例如:Socket通道数据是否已读入内存)
以下图为例解释一下:其中读取数据分为2个步骤,1. 内核等待数据可读,2. 将内核读到的数据拷贝到进程,其中第一个步骤为阻塞的(因为他在等待数据就绪),而第二个步骤为非阻塞的。
这张图在第一个步骤中,应用进程不断的询问内核,当数据没有就绪时,内核会立即返回,此时调用放线程也并未阻塞着等待数据就绪,可以做其他事情,所以第一个步骤为“异步非阻塞”的。
而第二个步骤,应用进程开始系统调用copy数据到用户空间,此时这个调用没有立即返回,再结合上面的分析,所以这个步骤为“同步非阻塞的”。
在Java中实现同步阻塞调用、异步阻塞调用、异步非阻塞调用:
同步阻塞调用:
public class Test {
public static void main(String[] args) throws InterruptedException {
/* 同步阻塞调用:
调用没有立即返回 => 同步
等待了数据就绪 => 阻塞
*/
int result = func();
func2(result);
}
public static int func() throws InterruptedException {
// 模拟(IO)阻塞
Thread.sleep(1000L);
// 模拟计算操作
return 1 + 1;
}
private static void func2(Integer result) {
// do something...
}
}
同步非阻塞演示不了
异步阻塞调用(此处使用join也能表现出来):
public class Test {
public static void main(String[] args) throws Exception {
/* 异步阻塞调用:
调用后立即返回 => 异步
返回后main线程直接获取结果,等待了数据就绪 => 阻塞
*/
FutureTask<Integer> futureTask = new FutureTask<>(Test::func);
new Thread(futureTask).run();
Integer result = futureTask.get();
func2(result);
}
private static int func() throws InterruptedException {
// 模拟(IO)阻塞
Thread.sleep(1000L);
// 模拟计算操作
return 1 + 1;
}
private static void func2(Integer result) {
// do something...
}
}
异步非阻塞调用:
public class Test {
public static void main(String[] args) throws Exception {
/* 异步非阻塞调用:
调用后立即返回 => 异步
调用后main线程直接就不管了,交由t1线程执行后续的操作,main线程也没有等待数据就绪 => 非阻塞
*/
CompletableFuture.supplyAsync(() -> func())
.thenAccept(result -> func2(result));
}
private static int func() {
// 模拟(IO)阻塞
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 模拟计算操作
return 1 + 1;
}
private static void func2(Integer result) {
// do something...
}
}