正确理解同步/异步和阻塞/非阻塞的区别:
Posted 儒雅随和狗粉丝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正确理解同步/异步和阻塞/非阻塞的区别:相关的知识,希望对你有一定的参考价值。
之前一直把异步和非阻塞混为一谈,在 了解BIO和NIO的区别时才发现了两者的差异,看了网上很多文章觉得说的总是差点意思,下面是个人理解:
1.同步 非同步 阻塞 非阻塞 概念
2.同步/非同步和阻塞/非阻塞的区别:
同步 异步 阻塞 非阻塞 区别_流水随波落花逐流的博客-CSDN博客_同步异步阻塞非阻塞
这篇将的比较中肯
3.BIO和NIO的区别:
总结:
注意: 异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。
1>同步:
主要指的是多个工作之间的协同,比如多个线程之间的协同,比如线程B需要等到线程A执行完,拿到线程A的结果线程B才能开始工作。这两个线程之间就是同步的。
2>同步阻塞/同步非阻塞:
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
方法B需要用到方法A的执行结果,比如这里是同步阻塞,在方法A中,执行到
(User) objectInputStream.readObject();对于同步阻塞来说,线程会停下来等待socket接收到数据才会往下运行,而对于同步非阻塞来说,方法A里的代码都执行过了,但是他不等待通信结果的返回,而是直接往下执行,这时候执行到方法B时,a==null就会抛出异常,这明显不是我们所想要的结果。
3>异步
同步和异步关注的是消息通信机制
这里主要疑惑的地方在于: 同步非阻塞和异步的区别:
比如下段代码分别采用同步非阻塞和异步去执行,
对于同步非阻塞: 当线程执行到方法B之后,什么时候socket通信返回结果线程是不清楚的,只能本线程去隔段时间去访问查看是否得到结果。
对于异步: 当线程执行到方法B之后,当socket通信返回结果后,会通知线程结果返回了比如有回调函数之类的通知本线程。
public static Integer A(){
...
//socket通信
Socker socket=new Socker(...);
ObjectInputStream objectInputStream=new ObjectInputStream(socket.getInputStream()
User user= (User) objectInputStream.readObject();
int res=user.getAge();
....
return res;
};
public static Integer B(Integer a){
if(a==null)
{
throw new RuntimeException("a is null");
}
...
};
public static void main(String[] args) {
Integer a=A();
Integer b=B(a);
}
4>放在BIO和NIO中进一步理解
对于使用NIO进行socket通信,编码非常麻烦,就比如需要等待通信结果
//代码不全 大概理解这段代码是sc.read(buf);会接收远程发送过来的数据
public void testAccept() throws Exception{
//创建服务器端的服务通道
ServerSocketChannel ssc =
ServerSocketChannel.open();
//绑定端口号
ssc.bind(new InetSocketAddress(8888));
//设置非阻塞模式
ssc.configureBlocking(false);
//调用accpet方法获取用户请求的连接通到
SocketChannel sc = ssc.accept();
System.out.println("有连接连入");
ByteBuffer buf = ByteBuffer.allocate(10);
sc.read(buf);
System.out.println("有数据读入:"+buf.toString());
}
不运行客户端,直接运行服务器,会发现输出 “有连接接入”,但是发现在sc.read(buf)行抛出了空指针异常。buf对象不可能为null,所以sc为null.
将SocketChannel sc = ssc.accept();改为:
while(sc==null){
sc = ssc.accept();
}
再次运行testAccept()方法,空指针的问题解决了;然后再运行testConnect()方法,发现连接能够正常建立,但是“有数据读入了。。”并没有输出,说明即使ssc服务通道设置了非阻塞,也没有改变得到的通道sc默认为阻塞模式,所以sc.read(buf)阻塞了。要不想让read()方法阻塞,需要在调用read()之前加sc.configureBlocking(false);这样即使没有读到数据,“有数据读入了。。”也能打印出来。
5>同步阻塞和非阻塞在NIO的实例对比:
非阻塞模式: 对于上面的场景,线程不会阻塞在read,会一直循环,可以一致处理用户连接请求,等客户端发送过来数据的话,如果允行到read()就会处理发送过来的数据,非阻塞模式下,因为这里设置乐循环,该线程会一直在这里循环这段代码,等运行到read()就处理客户端过来的数据,如果运行到accept()就会处理传过来的连接请求。
前面所讨论过,同步非阻塞和异步的区别就是,同步非阻塞需要自己打电话去询问是否书籍有货,这里的循环,当线程在循环中再次运行到accept()或read()就相当于询问是否有货,而异步的话就会通过回调函数等题型线程书籍到货。
测试代码: 阻塞和非阻塞的代码唯一区别是,设置 sc.configureBlocking(false); // 非阻塞模式,可以自行测试
// Server
public class SocketServer {
private static final Logger logger= LoggerFactory.getLogger(SocketServer.class);
public void start() throws IOException {
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(7788));
ByteBuffer buffer=ByteBuffer.allocate(16);
List<SocketChannel> channels=new ArrayList<>();
ssc.configureBlocking(false); // 非阻塞模式
while (true)
{
SocketChannel sc= ssc.accept();
logger.info("等待客户端连接");
if (sc!=null)
{
logger.info("客户端连接建立: "+sc.getRemoteAddress());
sc.configureBlocking(false); // 非阻塞模式
channels.add(sc);
}
for (SocketChannel channel:channels)
{
int read=channel.read(buffer);
if (read>0)
{
logger.info("接收客户端数据: "+sc.getRemoteAddress());
buffer.flip();
// ByteBufferUtil.debugRead(buffer);
buffer.clear();
}
}
}
}
public static void main(String[] args) throws IOException {
SocketServer socketServer=new SocketServer();
socketServer.start();
}
}
//Client
public class SocketClient {
static Logger logger= LoggerFactory.getLogger(SocketClient.class);
public void start() throws IOException {
SocketChannel sc=SocketChannel.open();
sc.connect(new InetSocketAddress("localhost",7788));
sc.write(ByteBuffer.wrap("Hello,Im ".getBytes()));
}
public static void main(String[] args) throws IOException {
SocketClient sc=new SocketClient();
sc.start();
}
}
以上是关于正确理解同步/异步和阻塞/非阻塞的区别:的主要内容,如果未能解决你的问题,请参考以下文章