正确理解同步/异步和阻塞/非阻塞的区别:

之前一直把异步和非阻塞混为一谈,在 了解BIO和NIO的区别时才发现了两者的差异,看了网上很多文章觉得说的总是差点意思,下面是个人理解:

1.同步 非同步 阻塞 非阻塞 概念

怎样理解阻塞非阻塞与同步异步的区别? - 知乎

2.同步/非同步和阻塞/非阻塞的区别:

同步 异步 阻塞 非阻塞 区别_流水随波落花逐流的博客-CSDN博客_同步异步阻塞非阻塞

这篇将的比较中肯

3.BIO和NIO的区别:

快速掌握NIO和BIO有何不同? - 知乎

总结:

注意: 异步必定是非阻塞的,所以不存在异步阻塞和异步非阻塞的说法。

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();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39552268/article/details/120200509