谈到并发编程就不得不提到NIO,以及相关的Java NIO框架Netty等,并且在很多面试中也经常提到NIO和AIO、同步和异步、阻塞和非阻塞等的区别。我先简短介绍下几个NIO相关的概念,然后再谈NIO重点掌握内容,以及Java NIO框架选型。
谈到并发编程就不得不提到NIO,以及相关的Java NIO框架Netty等,并且在很多面试中也经常提到NIO和AIO、同步和异步、阻塞和非阻塞等的区别。我先简短介绍下几个NIO相关的概念,然后再谈NIO重点掌握内容,以及Java NIO框架选型。
高并发编程系列
高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8)
高并发编程系列:CountDownLatch、Semaphore等4大并发工具类详解
直通BAT必考题系列:JVM性能调优的6大步骤,及关键调优参数详解
直通BAT必考题系列:7种JVM垃圾收集器特点,优劣势、及使用场景
高并发编程系列:并发容器的原理,7大并发容器详解、及使用场景
并发编程系列:Java线程池的使用方式,核心运行原理、以及注意事项
高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景
![]()
AIO、BIO、NIO的区别
IO模型主要分类:
1.BIO (同步阻塞I/O模式)
数据的读取写入必须阻塞在一个线程内等待其完成。
这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
2.NIO(同步非阻塞)
同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
3.AIO (异步非阻塞I/O模型)
异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
4.IO与NIO区别:
5.同步与异步的区别:
同步:发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生。
异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发。
6.阻塞和非阻塞
阻塞:传统的IO流都是阻塞式的。也就是说,当一个线程调用read()或者write()方法时,该线程将被阻塞,直到有一些数据读读取或者被写入,在此期间,该线程不能执行其他任何任务。在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量的客户端时,性能急剧下降。
非阻塞:Java
NIO是非阻塞式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。
7.BIO、NIO、AIO适用场景
NIO重点是把Channel(通道),Buffer(缓冲区),Selector(选择器)三个类之间的关系弄清楚。
1.缓冲区Buffer
Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。
在NIO中,所有的数据都是用缓冲区处理。这也就本文上面谈到的IO是面向流的,NIO是面向缓冲区的。
缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能于操作byte数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean)都对应一种缓冲区,具体如下:
2.通道Channel
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
因为Channel是全双工的,所以它比流更好地映射底层操作系统的API,特别是在UNIX网络编程中,底层操作系统的通道都是全双工的,同时支持读和写。
Channel有四种实现:
3.多路复用器Selector
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。
关键对象
监听注册
监听注册在Selector
socketChannel.register(selector, SelectionKey.OP_READ);
监听的事件有
1.NIO的应用
Java NIO成功的应用在了各种分布式、即时通信和中间件Java系统中,充分的证明了基于NIO构建的通信基础,是一种高效,且扩展性很强的通信架构。
例如:Dubbo(服务框架),就默认使用Netty作为基础通信组件,用于实现各进程节点之间的内部通信。
Jetty、Mina、Netty、Dubbo、ZooKeeper等都是基于NIO方式实现。
2.NIO框架
特别是Netty是目前最流行的一个Java开源框架NIO框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
相比JDK原生NIO,Netty提供了相对十分简单易用的API,非常适合网络编程。
Mina和Netty这两个NIO框架的创作者是同一个人Trustin Lee 。Netty从某种程度上讲是Mina的延伸和扩展,解决了一些Mina上的设计缺陷,也优化了一下Mina上面的设计理念。
另一方面Netty相比较Mina的优势:
Netty无疑是NIO框架的首选,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的,后续将重点详细谈Netty的实现原理以及实战场景。
高并发编程系列:NIO的详细介绍,3个核心设计、Java NIO框架选型
优知学院 2018-11-16 19:29:40
谈到并发编程就不得不提到NIO,以及相关的Java NIO框架Netty等,并且在很多面试中也经常提到NIO和AIO、同步和异步、阻塞和非阻塞等的区别。我先简短介绍下几个NIO相关的概念,然后再谈NIO重点掌握内容,以及Java NIO框架选型。
AIO、BIO、NIO的区别
IO模型主要分类:
1.BIO (同步阻塞I/O模式)
数据的读取写入必须阻塞在一个线程内等待其完成。
这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
2.NIO(同步非阻塞)
同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
3.AIO (异步非阻塞I/O模型)
异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
4.IO与NIO区别:
5.同步与异步的区别:
同步:发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生。
异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发。
6.阻塞和非阻塞
阻塞:传统的IO流都是阻塞式的。也就是说,当一个线程调用read()或者write()方法时,该线程将被阻塞,直到有一些数据读读取或者被写入,在此期间,该线程不能执行其他任何任务。在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量的客户端时,性能急剧下降。
非阻塞:Java NIO是非阻塞式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。
7.BIO、NIO、AIO适用场景
NIO重点是把Channel(通道),Buffer(缓冲区),Selector(选择器)三个类之间的关系弄清楚。
1.缓冲区Buffer
Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。
在NIO中,所有的数据都是用缓冲区处理。这也就本文上面谈到的IO是面向流的,NIO是面向缓冲区的。
缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能于操作byte数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean)都对应一种缓冲区,具体如下:
2.通道Channel
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
因为Channel是全双工的,所以它比流更好地映射底层操作系统的API,特别是在UNIX网络编程中,底层操作系统的通道都是全双工的,同时支持读和写。
Channel有四种实现:
3.多路复用器Selector
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。
关键对象
监听注册
监听注册在Selector
socketChannel.register(selector, SelectionKey.OP_READ);
监听的事件有
1.NIO的应用
Java NIO成功的应用在了各种分布式、即时通信和中间件Java系统中,充分的证明了基于NIO构建的通信基础,是一种高效,且扩展性很强的通信架构。
例如:Dubbo(服务框架),就默认使用Netty作为基础通信组件,用于实现各进程节点之间的内部通信。
Jetty、Mina、Netty、Dubbo、ZooKeeper等都是基于NIO方式实现。
2.NIO框架
特别是Netty是目前最流行的一个Java开源框架NIO框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
相比JDK原生NIO,Netty提供了相对十分简单易用的API,非常适合网络编程。
Mina和Netty这两个NIO框架的创作者是同一个人Trustin Lee 。Netty从某种程度上讲是Mina的延伸和扩展,解决了一些Mina上的设计缺陷,也优化了一下Mina上面的设计理念。
另一方面Netty相比较Mina的优势:
Netty无疑是NIO框架的首选,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的,后续将重点详细谈Netty的实现原理以及实战场景。
以上就是今天 Java NIO的详细介绍,除了从异步的角度应对并发,还需要更多架构的角度,利用分布式缓存、数据库拆分等方式解决高并发场景,详细的内容如下:
谈到并发编程就不得不提到NIO,以及相关的Java NIO框架Netty等,并且在很多面试中也经常提到NIO和AIO、同步和异步、阻塞和非阻塞等的区别。我先简短介绍下几个NIO相关的概念,然后再谈NIO重点掌握内容,以及Java NIO框架选型。
![]()
AIO、BIO、NIO的区别
IO模型主要分类:
1.BIO (同步阻塞I/O模式)
数据的读取写入必须阻塞在一个线程内等待其完成。
这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。
2.NIO(同步非阻塞)
同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
3.AIO (异步非阻塞I/O模型)
异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。
4.IO与NIO区别:
5.同步与异步的区别:
同步:发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生。
异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发。
6.阻塞和非阻塞
阻塞:传统的IO流都是阻塞式的。也就是说,当一个线程调用read()或者write()方法时,该线程将被阻塞,直到有一些数据读读取或者被写入,在此期间,该线程不能执行其他任何任务。在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量的客户端时,性能急剧下降。
非阻塞:Java
NIO是非阻塞式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。
7.BIO、NIO、AIO适用场景
NIO重点是把Channel(通道),Buffer(缓冲区),Selector(选择器)三个类之间的关系弄清楚。
1.缓冲区Buffer
Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。
在NIO中,所有的数据都是用缓冲区处理。这也就本文上面谈到的IO是面向流的,NIO是面向缓冲区的。
缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能于操作byte数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean)都对应一种缓冲区,具体如下:
2.通道Channel
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
因为Channel是全双工的,所以它比流更好地映射底层操作系统的API,特别是在UNIX网络编程中,底层操作系统的通道都是全双工的,同时支持读和写。
Channel有四种实现:
3.多路复用器Selector
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。
关键对象
监听注册
监听注册在Selector
socketChannel.register(selector, SelectionKey.OP_READ);
监听的事件有
1.NIO的应用
Java NIO成功的应用在了各种分布式、即时通信和中间件Java系统中,充分的证明了基于NIO构建的通信基础,是一种高效,且扩展性很强的通信架构。
例如:Dubbo(服务框架),就默认使用Netty作为基础通信组件,用于实现各进程节点之间的内部通信。
Jetty、Mina、Netty、Dubbo、ZooKeeper等都是基于NIO方式实现。
2.NIO框架
特别是Netty是目前最流行的一个Java开源框架NIO框架,Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
相比JDK原生NIO,Netty提供了相对十分简单易用的API,非常适合网络编程。
Mina和Netty这两个NIO框架的创作者是同一个人Trustin Lee 。Netty从某种程度上讲是Mina的延伸和扩展,解决了一些Mina上的设计缺陷,也优化了一下Mina上面的设计理念。
另一方面Netty相比较Mina的优势:
Netty无疑是NIO框架的首选,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的,后续将重点详细谈Netty的实现原理以及实战场景。
工具类是好的。提供机制但不提供策略,这是一个linux的编程哲学。我原来提到过,在某篇文章中看过GoF中的Gamma现在也更喜欢库而不是框架。
我现在编程已经不太看重这些了,当然因为必须在现有的语言、库和工具下工作,很自然的就会经常性的形成类似于框架的东西,但是我会经常问自己是否能够避免:如果去框架化不会带来更多的麻烦,我就会把它拆掉。
认知
那框架完全是废物吗?非也。能产生这种东西,就表明它必定有它自己的用途。我们有一个业务,这个业务有它自己的模型、规则、流程等等。但是有些不确定的东西会未来才接进来,那么我们就非得有一个框架不可:比如图形界面就是这样。【1】
不过更多时候我发现事情真的有更简单的办法,把机制或者功能集合中的一些元素粘合一下,这样很简单,产生变化的时候重构的工作量也小。而且并不影响重用,或者说能够更好的重用(因为各部分粒度比较小)。
关键是真正认识我们要处理的业务的实质是什么。这个实质不是指这个业务对人类来说是什么,而是说这个业务在现有的不能抛弃的抽象层上是什么。
对我当前的目标(记住这是可变化的)而言,这个抽象层包括计算机本身、操作系统、基础库和通用的协议。要注意的是,抽象层在某种意义上并不是真的抽象,而是一种具体化。比如你有2G内存,而不是无限的图灵机纸带,比如硬盘很慢;比如软件根据实际情况做出的妥协。
看起来四处都是抽象层,所以上面强调了,“不能抛弃”。过去我很擅长.NET,.NET及围绕它产生的一系列环境是我用过的可以说最舒服的编程基础设施。我也对ASP.NET极其熟悉,基本上可以弄清楚每一个细节。但我最终学会的一件事就是它不是一个不能抛弃的部分。
这说到最后,还是它提供的东西带来的便捷和它按照它的作者的意愿具体化的部分带来的限制或麻烦之间的一个权衡。所有的Web业务从根本上就是按照一定的协议和文档格式(比如HTTP/HTML,又是一个抽象层,但我们不能抛弃,否则没人能用我们的东西)根据参数生成字符串的这么一件事情。
决策
当HTTP和HTML这些东西设计定稿的时候,可以说所有的规划就做完了:在这个基础上有足够的约定和共识落在了纸面上,最后又变成了机制和策略。只要不破坏他们,事情就玩的转;剩下的东西就是多加进去的(这和图形界面框架的必要性形成了一个对比),就必须“给个理由先”。
很多人不愿意这样看问题,包括最初我沉溺在ASP.NET中的时候。因为这会让我们感觉回到了ASP时代,是一种倒退;如果跟别人交流,看起来好像我们土的掉渣、不求上进。我不知道我们是如何产生这种感情的,也许是因为在我们经验比较少的时候,我们就会不够自信、不够坚定。【2】
有一点也是必须要认识到的,不回答这个问题,我们不能说服自己:大家都在用ASP.NET或XX。这其实也就说明了ASP.NET是给谁做的。更多的,我们就必须回答:我当下处境和“大家”一样吗?这个答案决定了我们要不要动手去做:也就说,技术选型。既然有这个词,就有这个词对应的工作量。
对于这个词需要明确的是,我们选的是“型”,而不是若干个成品。只有比较“型”和成品的符合程度,才能得出结论:也许是在人家的框架上做一些库就非常舒服了,也许是做一个自己的框架,更加适合自己的需求。或者不定义任何策略,提供一堆机制,根据问题现场组装。
现在我们决定做一个框架了,我们一直在用ASP.NET,关键是我们在一个别人画的圈子里,玩不出太大的花来。当然,我们的目的不是玩花,而是肯定先要感受到需求。那么我感受到需求了,我觉得ASP.NET束缚了我,我怎么办呢?
执行
首先明确一点:如果说我们承认框架是有用的,我认为框架也应该尽量小(或者尽量空),大多数情况下应该属于可以以较小代价推翻的东西。
而且一个值得注意的要点是,不能把功能和它揉在一起做;因为这样我们会最大概率的得到Linus所谓“漂亮的坚固堡垒”,碰着点真正的(相对于假设的)变化就会歇菜。一个个人经验是,更加重视局部输入输出的明确简单,少玩精巧的对象交互,使组织部分成为一个灵活的粘合层。
具体到要在ASP.NET下做真正的框架,我的建议是从Handler和Module上做,不要用一切微软制订的更上层的东西:WebForm、MVC,这对于框架作者来说不是什么有用的东西,它们逼你削足适履。
我们首先要有自由,才能够把需求让我们产生的那个想象的东西按它本来应该有的样子画出来。另一方面是信心,我们总会有这样那样的顾虑让人裹足不前。
有些人觉得编译器难写、操作系统难写;有些人觉得ASP.NET的作者们写的那些东西难写。我能说的是,凡是ASP.NET实现了的东西,都是没有什么难度的(这就使得工作变得可评估)。无论从技术细节上来说,还是从设计上来说。
对于后者,恰恰仅仅是ASP.NET的作者们,他们自己收集了若干需求,然后产生了一些这个东西应该这样那样的想法。我们不应该被这些影响:除非我们不确信我们的需求是真的,否则我们就不会做出比他们更差的决定。尤其是目标越明确越具体,我们的设计就会越有优越性。
在这个基础上,信心怎么来呢?我们不启动,老在那里准备,永远没有信心。事实上从最初开始,比如继承一下Handler的接口,打印一行Hello world,就能极大的增强信心:剩下的事情就是如何把这行字换成更有用的东西:这部分如何组织,这就是你的框架了。
最后
当然,我们必须有明确的需求,而不是受了别人的影响就说我要做一个MVC、我要更好玩转那些Entities;我们读到的东西更多的是一种参考,有启发就想想没有就暂时扔掉。关键是说我有一个点子,它能让我们生活(至少是我眼前关注的这块业务)的更容易;就是这样。
最后,程序员或开发人员,往往也是编程的爱好者。在这种情况下,只要闲得没事干,自己做一些东西,哪怕只是一个原型,也总是好的。只是需要记住一点,首先要确定我们要解决什么问题:放空炮能得到的益处可就要小多了。
------
注【1】:比较图形界面框架和图形库的区别就可以知道,前者为了最终用户有一个直观、统一的感受,必须先规划一些东西。
注【2】:话说回来,如果一开始就自信满满,也可能会错过很多东西,最终真的变成一个老冒也不一定。有的时候反复的过程是必要的:有人也许天生能做到极其快速直接的筛选出有用信息排除无用的;但至少我知道我自己是很笨的,很多时候都得试一试才能学到点什么。当然,笨人也可以慢慢掌握更好的判断方法。