java中的IO主要源自于网络和本地文件
IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO
在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。
BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。
HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。
BIO是一个连接一个线程。
NIO是一个请求一个线程。
AIO是一个有效请求一个线程。
========http://xm-king.iteye.com/blog/766330
最近一直在忙着JAVA NIO的知识,花了一下午的时间,总算写出了一个可以运行的程序,废话少说,上代码!
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- 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.Iterator;
- import java.util.Set;
- public class NIOServer {
- /*标识数字*/
- private int flag = 0;
- /*缓冲区大小*/
- private int BLOCK = 4096;
- /*接受数据缓冲区*/
- private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
- /*发送数据缓冲区*/
- private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
- private Selector selector;
- public NIOServer(int port) throws IOException {
- // 打开服务器套接字通道
- ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
- // 服务器配置为非阻塞
- serverSocketChannel.configureBlocking(false);
- // 检索与此通道关联的服务器套接字
- ServerSocket serverSocket = serverSocketChannel.socket();
- // 进行服务的绑定
- serverSocket.bind(new InetSocketAddress(port));
- // 通过open()方法找到Selector
- selector = Selector.open();
- // 注册到selector,等待连接
- serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
- System.out.println("Server Start----8888:");
- }
- // 监听
- private void listen() throws IOException {
- while (true) {
- // 选择一组键,并且相应的通道已经打开
- selector.select();
- // 返回此选择器的已选择键集。
- Set<SelectionKey> selectionKeys = selector.selectedKeys();
- Iterator<SelectionKey> iterator = selectionKeys.iterator();
- while (iterator.hasNext()) {
- SelectionKey selectionKey = iterator.next();
- iterator.remove();
- handleKey(selectionKey);
- }
- }
- }
- // 处理请求
- private void handleKey(SelectionKey selectionKey) throws IOException {
- // 接受请求
- ServerSocketChannel server = null;
- SocketChannel client = null;
- String receiveText;
- String sendText;
- int count=0;
- // 测试此键的通道是否已准备好接受新的套接字连接。
- if (selectionKey.isAcceptable()) {
- // 返回为之创建此键的通道。
- server = (ServerSocketChannel) selectionKey.channel();
- // 接受到此通道套接字的连接。
- // 此方法返回的套接字通道(如果有)将处于阻塞模式。
- client = server.accept();
- // 配置为非阻塞
- client.configureBlocking(false);
- // 注册到selector,等待连接
- client.register(selector, SelectionKey.OP_READ);
- } else if (selectionKey.isReadable()) {
- // 返回为之创建此键的通道。
- client = (SocketChannel) selectionKey.channel();
- //将缓冲区清空以备下次读取
- receivebuffer.clear();
- //读取服务器发送来的数据到缓冲区中
- count = client.read(receivebuffer);
- if (count > 0) {
- receiveText = new String( receivebuffer.array(),0,count);
- System.out.println("服务器端接受客户端数据--:"+receiveText);
- client.register(selector, SelectionKey.OP_WRITE);
- }
- } else if (selectionKey.isWritable()) {
- //将缓冲区清空以备下次写入
- sendbuffer.clear();
- // 返回为之创建此键的通道。
- client = (SocketChannel) selectionKey.channel();
- sendText="message from server--" + flag++;
- //向缓冲区中输入数据
- sendbuffer.put(sendText.getBytes());
- //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
- sendbuffer.flip();
- //输出到通道
- client.write(sendbuffer);
- System.out.println("服务器端向客户端发送数据--:"+sendText);
- client.register(selector, SelectionKey.OP_READ);
- }
- }
- /**
- * @param args
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- // TODO Auto-generated method stub
- int port = 8888;
- NIOServer server = new NIOServer(port);
- server.listen();
- }
- }
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.SelectionKey;
- import java.nio.channels.Selector;
- import java.nio.channels.SocketChannel;
- import java.util.Iterator;
- import java.util.Set;
- public class NIOClient {
- /*标识数字*/
- private static int flag = 0;
- /*缓冲区大小*/
- private static int BLOCK = 4096;
- /*接受数据缓冲区*/
- private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
- /*发送数据缓冲区*/
- private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
- /*服务器端地址*/
- private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress(
- "localhost", 1111);
- public static void main(String[] args) throws IOException {
- // TODO Auto-generated method stub
- // 打开socket通道
- SocketChannel socketChannel = SocketChannel.open();
- // 设置为非阻塞方式
- socketChannel.configureBlocking(false);
- // 打开选择器
- Selector selector = Selector.open();
- // 注册连接服务端socket动作
- socketChannel.register(selector, SelectionKey.OP_CONNECT);
- // 连接
- socketChannel.connect(SERVER_ADDRESS);
- // 分配缓冲区大小内存
- Set<SelectionKey> selectionKeys;
- Iterator<SelectionKey> iterator;
- SelectionKey selectionKey;
- SocketChannel client;
- String receiveText;
- String sendText;
- int count=0;
- while (true) {
- //选择一组键,其相应的通道已为 I/O 操作准备就绪。
- //此方法执行处于阻塞模式的选择操作。
- selector.select();
- //返回此选择器的已选择键集。
- selectionKeys = selector.selectedKeys();
- //System.out.println(selectionKeys.size());
- iterator = selectionKeys.iterator();
- while (iterator.hasNext()) {
- selectionKey = iterator.next();
- if (selectionKey.isConnectable()) {
- System.out.println("client connect");
- client = (SocketChannel) selectionKey.channel();
- // 判断此通道上是否正在进行连接操作。
- // 完成套接字通道的连接过程。
- if (client.isConnectionPending()) {
- client.finishConnect();
- System.out.println("完成连接!");
- sendbuffer.clear();
- sendbuffer.put("Hello,Server".getBytes());
- sendbuffer.flip();
- client.write(sendbuffer);
- }
- client.register(selector, SelectionKey.OP_READ);
- } else if (selectionKey.isReadable()) {
- client = (SocketChannel) selectionKey.channel();
- //将缓冲区清空以备下次读取
- receivebuffer.clear();
- //读取服务器发送来的数据到缓冲区中
- count=client.read(receivebuffer);
- if(count>0){
- receiveText = new String( receivebuffer.array(),0,count);
- System.out.println("客户端接受服务器端数据--:"+receiveText);
- client.register(selector, SelectionKey.OP_WRITE);
- }
- } else if (selectionKey.isWritable()) {
- sendbuffer.clear();
- client = (SocketChannel) selectionKey.channel();
- sendText = "message from client--" + (flag++);
- sendbuffer.put(sendText.getBytes());
- //将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
- sendbuffer.flip();
- client.write(sendbuffer);
- System.out.println("客户端向服务器端发送数据--:"+sendText);
- client.register(selector, SelectionKey.OP_READ);
- }
- }
- selectionKeys.clear();
- }
- }
- }
个人感觉,JAVA NIO的操作时麻烦了不少,但是无疑这样做效率会得到很大的提升。