Java目前有三种IO相关的API了,下面简单的说一下:

BIO,阻塞IO,最常用的Java IO API,提供一般的流的读写功能。相信学习Java的人,都用过。

NIO,非阻塞IO,在JDK1.4中开始出现,大量应用与服务器端编程,用于提高并发访问的性能,常用的NIO框架有Netty,Mina。

AIO,异步IO,在JDK1.7开始出现。还没有了解过,等以后了解了再说。

阻塞、非阻塞,同步、异步

在写这篇文章前,在网上了解了一下,其中争议最的问题要数阻塞、非阻塞怎么理解,异步、同步怎么理解。

由于每个人想法的不同,很难达到一个一致的答案,又没有真正的大牛出来给这一个准确的定义。这里也简单的说一下,我对这两组名词的理解。

1)阻塞、非阻塞

我认为,BIO,NIO没有大家想的那么复杂,就是底层实现中进行数据的读写(IO)采取的两种方案,只不过非阻塞读写要比阻塞IO读写更快一些。

bio中的InputStream#read()是一个block方法。

2)同步、异步

同步与异步,我认为说的并不是IO本身,我认为说的是程序采用的编程模型,也就是说采用的是同步的编程模型还是异步的编程模型。

BIO、NIO,他们的区别是操作系统读写数据采用的方式,他们是Java中的概念,在Java领域,他们的底层实现采用的是同步的编程模型。所以说BIO、NIO都是同步的。

AIO的底层实现应当是异步的编程模型,所以说它是异步IO。

这里我只是阐述了我对它们的理解,没有与大家争论到底怎么去理解他们。也许我没有大家想的那么深远,毕竟我只是学习了NIO不到一天时间而已。

针对BIO、NIO,服务器编程如何提高性能

一个程序运行的快慢,一般有会受到两个因素的影响:1)程序代码是否高效,2)IO读写是否高效。曾经看过这么一幅图,大致内容是:一帮不同角色的人(程序员、运维、项目经理等角色的人)在一起讨论一个应用程序效率地下的问题。

程序员说的是:给我3个月时间,我能够让程序运行效率提高,当然了,我要调整代码的整体结构…

运维说:…

项目经理说:换用读写更快的硬件设备解决这个问题。

故事我已经无法还原,但是这个故事说的内容就是程序优化带来的效率的提升远不及提高IO速度带来的提升。

相比于BIO,NIO就是从读写来提升效率的。性能对于服务器来说尤为重要,服务器端编程并不是都采用了NIO编程。

Tomcat服务器内部,就有BIO、NIO两种方式。

1)BIO如何提高并发访问

BIO,是一种阻塞IO,服务器端使用BIO进行数据读写时,一般都是采用了一个Socket请求对应一个Thread的方式来提高性能的。

但是一台服务器上,可以跑的线程数量也是有限制的:线程不是越多越好,毕竟线程间的切换,也是有不小的开销。也不是越少越好,线程太少,极端情况下一个线程,如果用一个线程来解决用户的并发访问,服务器接收一个客户的请求时,其他人都要处于等待状态。你访问网页,多数情况下超过5秒,估计你就关掉它了吧。

  或者采用线程池方案。

2)采用NIO编程时 如何提高并发访问

采用选择器轮询可用通道,读写数据。具体的怎么做的就不说了,网上一大坨一大坨的,虽然网上大家写的大多是copy别人的。下面给会出一个例子,所以这里就不多说了,不知道的可以网上找相关的文章。

一个Thread下开一个Selector,一个Selector处理多个Socket通道(也就是多个用于请求),这样就是一个Thread线程可以同时处理多个用户请求。

孰优孰劣

假若说,服务器设置同时处理1000个用户请求(也就是1000个处理用户请求的线程)。假若有10000个人来发请求。

如果采用BIO API编程,那么就同时只能为1000个人服务,其他的9000人就处于等待状态。

如果采用NIO API编程,也开启1000个线程,因为一个Thread可以同时处理多个用户请求,咱不说让它处理太多了,就处理10个吧,这样算下来,这个10000个用户请求,就都可以处理了。

BIO(客户端)与NIO(服务端)通信

今天学习了NIO,就用NIO来处理浏览器用户请求吧。浏览器发送的肯定不是采用NIO API发送Socket请求的,肯定是使用了阻塞式IO,也就是对应于Java中的BIO了。

package com.fjn.other.nio.socket;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

import java.net.Socket;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.ServerSocketChannel;

import java.nio.channels.SocketChannel;

import java.util.ArrayList;

import java.util.Collection;

import java.util.Iterator;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

@SuppressWarnings({ "unchecked" })

public class NioServer {

       ServerSocketChannel serverChannel;

       ServerSocket serverSocket;

       public final int port;

       private Selector selector;

       ByteBuffer buffer = ByteBuffer.allocate(1024);

       NioServer(final int port) {

              this.port = port;

       }

       void init() throws Exception {

              // 创建 ServerSocketChannel、ServerSocket

              serverChannel = ServerSocketChannel.open();

              serverSocket = serverChannel.socket();

              serverSocket.bind(new InetSocketAddress(port));

              // 设置通道为非阻塞模式

              serverChannel.configureBlocking(false);

              // 开启通道选择器,并注册 ServerSocketChannel

              selector = Selector.open();

              serverChannel.register(selector, SelectionKey.OP_ACCEPT);

       }

       void go() throws Exception {

              while (true) {

                     int num = selector.select();

                     if (num <= 0)

                            continue;

                     Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

                     while (keyIter.hasNext()) {

                            final SelectionKey key = keyIter.next();

                            // 接收一个Socket连接

                            // key.isAcceptable()如果为true,说明channnel支持accept(),也就是说明是一个ServerSocketChannel

                            if (key.isAcceptable()) {

                                   SocketChannel clientChannel = serverChannel.accept();

                                   if (clientChannel != null) {

                                          clientChannel.configureBlocking(false);

                                          clientChannel.register(selector, SelectionKey.OP_READ

                                                        | SelectionKey.OP_WRITE);

                                   }

                            }

                            // 如果isReadable()为true,说明是一个SocketChannel

                            if (key.isReadable()) {

                                   String requestContent = read(key);

                                   // 业务处理

                                   // responseContent=doSomthing(requestContent);

                                   write(key, "ok" /* responseContent */);

                            }

                            keyIter.remove();

                     }

              }

       }

       // 从通道读取数据

       String read(SelectionKey key) throws Exception {

              SocketChannel socketChannel = (SocketChannel) key.channel();

              buffer.clear();// 这一步必须有

              int len = 0;

              StringBuffer str=new StringBuffer();

              while ((len = socketChannel.read(buffer)) > 0) {

                     byte[] bs = buffer.array();

                     String block=new String(bs, 0, len);

                     System.out.println("Server read: " + block);

                     str.append(block);

              }

              buffer.clear();

              return str.toString();

       }

       // 写数据到通道

       void write(SelectionKey key, String str) throws Exception {

              SocketChannel socketChannel = (SocketChannel) key.channel();

              buffer.clear();

              buffer.put(str.getBytes());

              buffer.flip();// 这一步必须有

              socketChannel.write(buffer);

       }

       public static void main(String[] args) throws Exception {

              final int port = 10000;

              NioServer server = new NioServer(port);

              server.init();

///========================================================

              // 接下来模拟3个Client并发访问服务器

              int poolsize = 3;

              ExecutorService pool = Executors.newFixedThreadPool(poolsize);

              Collection<Callable> tasks = new ArrayList<Callable>(10);

              final String clientname="clientThread";

              for (int i = 0; i < poolsize; i++) {

                     final int n = i;

                     // 若每一个Client都保持使用BIO方式发送数据到Server,并读取数据。

                     tasks.add(new Callable() {

                            @Override

                            public Object call() throws Exception {

                                   Socket socket = new Socket("127.0.0.1", port);

                                   final InputStream input = socket.getInputStream();

                                   final OutputStream out = socket.getOutputStream();

                                   final String clientname_n = clientname + "_" + n;

                                   // BIO读取数据线程

                                   new Thread(clientname_n + "_read") {

                                          @Override

                                          public void run() {

                                                 byte[] bs = new byte[1024];

                                                 while (true) {

                                                        try {

                                                               Thread.sleep(1000);

                                                        } catch (InterruptedException e) {

                                                               e.printStackTrace();

                                                        }

                                                        int len = 0;

                                                        try {

                                                               while ((len = input.read(bs)) != -1) {

                                                                      System.out.println("Clinet thread "

                                                                                    + Thread.currentThread()

                                                                                                  .getName() + " read: "

                                                                                    + new String(bs, 0, len));

                                                               }

                                                        } catch (IOException e) {

                                                               e.printStackTrace();

                                                        }

                                                 }

                                          }

                                   }.start();

                                   // BIO写数据线程

                                   new Thread(clientname_n + "_write") {

                                          @Override

                                          public void run() {

                                                 int a = 0;

                                                 while (true) {

                                                        try {

                                                               Thread.sleep(100);

                                                        } catch (InterruptedException e) {

                                                               e.printStackTrace();

                                                        }

                                                        String str = Thread.currentThread().getName()

                                                                      + " hello, " + a;

                                                        try {

                                                               out.write(str.getBytes());

                                                               a++;

                                                        } catch (IOException e) {

                                                               e.printStackTrace();

                                                        }

                                                 }

                                          }

                                   }.start();

                                   return null;

                            }

                     });

              }

              pool.invokeAll((Collection<? extends Callable<Object>>) tasks);

              server.go();

       }

}

上面的测试的是3个Client采用BIO API不断的并发的发送Socket 请求到Server端。Server采用NIO API处理Client的请求并作出响应,然后Client接收响应。

Java Se : Java NIO(服务端)与BIO(客户端)通信的更多相关文章

  1. JAVA RPC (十) nio服务端解析

    源码地址:https://gitee.com/a1234567891/koalas-rpc 企业生产级百亿日PV高可用可拓展的RPC框架.理论上并发数量接近服务器带宽,客户端采用thrift协议,服务 ...

  2. Java网络编程(TCP服务端)

    /* * TCP服务端: * 1.创建服务端socket服务,并监听一个端口 * 2.服务端为了给客户端提供服务,获取客户端的内容,可以通过accept方法获取连接过来的客户端对象 * 3.可以通过获 ...

  3. “快的打车”创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - V2EX

    "快的打车"创始人陈伟星的新项目招人啦,高薪急招Java服务端/Android/Ios 客户端研发工程师/ mysql DBA/ app市场推广专家,欢迎大家加入我们的团队! - ...

  4. java开源即时通讯软件服务端openfire源码构建

    java开源即时通讯软件服务端openfire源码构建 本文使用最新的openfire主干代码为例,讲解了如何搭建一个openfire开源开发环境,正在实现自己写java聊天软件: 编译环境搭建 调试 ...

  5. java http post/get 服务端和客户端实现json传输

    注:本文来源于<java http post/get 服务端和客户端实现json传输> 最近需要写http post接口所以学习下. 总的还是不难直接上源码! PostHttpClient ...

  6. Java的oauth2.0 服务端与客户端的实现

    oauth原理简述 oauth本身不是技术,而是一项资源授权协议,重点是协议!Apache基金会提供了针对Java的oauth封装.我们做Java web项目想要实现oauth协议进行资源授权访问,直 ...

  7. java网络编程TCP传输—流操作—服务端反馈与客户端接收

    在读取完流后,服务端会向客户端返回一些数据,告诉客户端,已经写完了. 在这里和”流操作—拿到源后的写入动作“差不多,客户端同样以byte与Buffered两种缓冲读取作为例子,同时,.也是希望大家给补 ...

  8. NIO 服务端TCP连接管理的方案

    最近做的一个项目需要在服务端对连接端进行管理,故将方案记录于此. 方案实现的结果与背景 因为服务端与客户端实现的是长连接,所以需要对客户端的连接情况进行监控,防止无效连接占用资源. 完成类似于心跳的接 ...

  9. 客户端(springmvc)调用netty构建的nio服务端,获得响应后返回页面(同步响应)

    后面考虑通过netty做一个真正意义的简约版RPC框架,今天先尝试通过正常调用逻辑调用netty构建的nio服务端并同步获得返回信息.为后面做铺垫 服务端实现 我们先完成服务端的逻辑,逻辑很简单,把客 ...

  10. NIO服务端主要创建过程

    NIO服务端主要创建过程:   步骤一:打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的副管道,示例代码如下:      ServerSocketChannel ...

随机推荐

  1. EL表达式之sessionScope

    EL 全名为Expression Language EL 语法很简单,它最大的特点就是使用上很方便.接下来介绍EL主要的语法结构: ${sessionScope.user.sex} 所有EL都是以 $ ...

  2. Scalaz(5)- typeclass:my typeclass scalaz style-demo

    我们在上一篇讨论中介绍了一些基本的由scalaz提供的typeclass.这些基本typeclass主要的作用是通过操作符来保证类型安全,也就是在前期编译时就由compiler来发现错误.在这篇讨论中 ...

  3. Hibernate总结(二)

    在上一篇Hibernate总结(一)简单总结了一级缓存,快照,增删改查的简单使用,这一篇总结两张表的级联操作. 级联涉及到三种情况,many-many,1-many,many-1. 首先是1-many ...

  4. 6to5 – 让你即刻体验 ECMAScript 6 编程

    ECMAScript 6 是下一代的 ECMAScript 标准.ECMAScript 6 的目标是让 JavaScript 可以用来编写复杂的应用程序.函数库和代码的自动生成器. ES6 是这门语言 ...

  5. Fixed Responsive Nav – 响应式的单页网站导航插件

    Fixed Responsive Nav 是一个响应式的,固定的,触摸友好的单页网站导航插件,响应式导航,流畅的动画滚动.该项目采用渐进增强构建,支持工作在 IE6 及以上版本的浏览器. 你可以给导航 ...

  6. [js开源组件开发]localStorage-cache本地存储的缓存管理

    localStorage-cache本地存储的缓存管理 距离上次的组件开发有近三个月的时间了,最近一直在做一些杂事,无法静下心来写写代码,也是在学习emberjs,在emberjs中有一个很重要的东西 ...

  7. .net c# 视频剪切抓取缩略图

    public string Cut(string ffmpegPath, string videoPath, string savePath, string imgSize, int sleepTim ...

  8. windows Python 3.4.3 安装图文

    1.去官网(https://www.python.org/downloads/)下载软件. 2.运行安装程序: 下一步 next. 下一步 next 全部选中,下一步 next. 安装中..来自:ht ...

  9. 尝试加载 Oracle 客户端库时引发 BadImageFormatException。问题记录

    电脑是win8 64位,安装oracle 11g r2 64位的,谁知道一切装完毕后,打开项目却连不上oracle数据了...首先是pl/sql连不上,装了oracle服务器,应该是不用再装客户端,p ...

  10. kindeditor-在线编辑器

    写在前面的话: 今天是第一次写博客,很值得纪念,希望能够和大神们一起交流技术,一起进步...来自<一只有梦想的前端小白> 最近项目中需要实现图文混排的效果,所以研究了下在线编辑器-- ki ...