Java NIO之选择就绪模式

开发 后端
Java NIO出现不只是一个技术性能的提高,你会发现网络上到处在介绍它,因为它具有里程碑意义,从JDK1.4开始,Java开始提高性能相关的功能,从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。

Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。

Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。

Java NIO出现不只是一个技术性能的提高,你会发现网络上到处在介绍它,因为它具有里程碑意义,从JDK1.4开始,Java开始提高性能相关的功能,从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。


图 1 类结构图

 

  1. package cn.chenkangxian.nioconcurrent; 
  2.  
  3. import java.io.IOException; 
  4. import java.nio.ByteBuffer; 
  5. import java.nio.channels.SelectionKey; 
  6. import java.nio.channels.SocketChannel; 
  7. import java.util.LinkedList; 
  8. import java.util.List; 
  9.  
  10. /** 
  11.  * @Project: testNio 
  12.  *  
  13.  * @Author: chenkangxian 
  14.  *  
  15.  * @Annotation: 使用线程池来处理大量channel并发 
  16.  *  
  17.  * @Date:2011-7-5 
  18.  *  
  19.  * @Copyright: 2011 chenkangxian, All rights reserved. 
  20.  *  
  21.  */ 
  22. public class SelectSocketsThreadPool extends SelectSockets { 
  23.  
  24.     private static final int MAX_THREADS = 5
  25.     private ThreadPool pool = new ThreadPool(MAX_THREADS); 
  26.  
  27.     /** 
  28.      * 从socket中读数据 
  29.      */ 
  30.     protected void readDataFromSocket(SelectionKey key) throws Exception { 
  31.         WorkerThread worker = pool.getWorker(); 
  32.         if (worker == null) { 
  33.             return
  34.         worker.serviceChannel(key); 
  35.     } 
  36.     /** 
  37.      *   
  38.      * @Project: concurrentnio 
  39.      * 
  40.      * @Author: chenkangxian 
  41.      * 
  42.      * @Annotation:线程池 
  43.      * 
  44.      * @Date:2011-7-20 
  45.      * 
  46.      * @Copyright: 2011 chenkangxian, All rights reserved. 
  47.      * 
  48.      */ 
  49.     private class ThreadPool { 
  50.         List idle = new LinkedList(); 
  51.         /** 
  52.          * 线程池初始化 
  53.          *  
  54.          * @param poolSize 线程池大小 
  55.          */ 
  56.         ThreadPool(int poolSize) { 
  57.             for (int i = 0; i < poolSize; i++) { 
  58.                 WorkerThread thread = new WorkerThread(this); 
  59.                 thread.setName("Worker" + (i + 1)); 
  60.                 thread.start(); 
  61.                 idle.add(thread); 
  62.             } 
  63.         } 
  64.         /** 
  65.          * 获得工作线程 
  66.          *  
  67.          * Author: chenkangxian 
  68.          * 
  69.          * Last Modification Time: 2011-7-20 
  70.          * 
  71.          * @return 
  72.          */ 
  73.         WorkerThread getWorker() { 
  74.             WorkerThread worker = null
  75.  
  76.             synchronized (idle) { 
  77.                 if (idle.size() > 0) { 
  78.                     worker = (WorkerThread) idle.remove(0); 
  79.                 } 
  80.             } 
  81.             return (worker); 
  82.         } 
  83.         /** 
  84.          * 送回工作线程 
  85.          *  
  86.          * Author: chenkangxian 
  87.          * 
  88.          * Last Modification Time: 2011-7-20 
  89.          * 
  90.          * @param worker 
  91.          */ 
  92.         void returnWorker(WorkerThread worker) { 
  93.             synchronized (idle) { 
  94.                 idle.add(worker); 
  95.             } 
  96.         } 
  97.     } 
  98.     private class WorkerThread extends Thread { 
  99.         private ByteBuffer buffer = ByteBuffer.allocate(1024); 
  100.         private ThreadPool pool; 
  101.         private SelectionKey key; 
  102.         WorkerThread(ThreadPool pool) { 
  103.             this.pool = pool; 
  104.         } 
  105.         public synchronized void run() { 
  106.             System.out.println(this.getName() + " is ready"); 
  107.             while (true) { 
  108.                 try { 
  109.                     this.wait();//等待被notify 
  110.                 } catch (InterruptedException e) { 
  111.                     e.printStackTrace(); 
  112.                     this.interrupt(); 
  113.                 } 
  114.                 if (key == null) {//直到有key 
  115.                     continue
  116.                 } 
  117.                 System.out.println(this.getName() + " has been awakened"); 
  118.                 try { 
  119.                     drainChannel(key); 
  120.                 } catch (Exception e) { 
  121. System.out.println("Caught '" + e + "' closing channel"); 
  122.                     try { 
  123. key.channel().close(); 
  124.                     } catch (IOException ex) { 
  125.     ex.printStackTrace(); 
  126.                     } 
  127.                     key.selector().wakeup(); 
  128.                 } 
  129.                 key = null
  130.                 this.pool.returnWorker(this); 
  131.             } 
  132.         } 
  133.         synchronized void serviceChannel(SelectionKey key) { 
  134.             this.key = key; 
  135.             //消除读的关注 
  136.             key.interestOps(key.interestOps() & (~SelectionKey.OP_READ)); 
  137.             this.notify(); 
  138.         } 
  139.         void drainChannel(SelectionKey key) throws Exception { 
  140.             SocketChannel channel = (SocketChannel) key.channel(); 
  141.             int count; 
  142.             buffer.clear();  
  143.             while ((count = channel.read(buffer)) > 0) { 
  144.                 buffer.flip(); 
  145.                 while (buffer.hasRemaining()) { 
  146.                     channel.write(buffer); 
  147.                 } 
  148.                 buffer.clear(); 
  149.             } 
  150.             if (count < 0) { 
  151.                 channel.close(); 
  152.                 return
  153.             } 
  154.             //重新开始关注读事件 
  155.             key.interestOps(key.interestOps() | SelectionKey.OP_READ); 
  156.             key.selector().wakeup(); 
  157.         } 
  158.     } 
  159.     public static void main(String[] args) throws Exception { 
  160.         new SelectSocketsThreadPool().go(args); 
  161.     } 

 

  1. package cn.chenkangxian.nioconcurrent; 
  2. import java.net.InetSocketAddress; 
  3. import java.net.ServerSocket; 
  4. import java.nio.ByteBuffer; 
  5. import java.nio.channels.SelectableChannel; 
  6. import java.nio.channels.SelectionKey; 
  7. import java.nio.channels.Selector; 
  8. import java.nio.channels.ServerSocketChannel; 
  9. import java.nio.channels.SocketChannel; 
  10. import java.util.Iterator; 
  11. /** 
  12.  *  
  13.  * @Project: concurrentnio 
  14.  * 
  15.  * @Author: chenkangxian 
  16.  * 
  17.  * @Annotation:  
  18.  * 
  19.  * @Date:2011-7-11 
  20.  * 
  21.  * @Copyright: 2011 chenkangxian, All rights reserved. 
  22.  * 
  23.  */ 
  24. public class SelectSockets { 
  25.     public static int PORT_NUMBER = 1234
  26.     private ByteBuffer buffer = ByteBuffer.allocate(1024); 
  27.     public static void main(String[] args) throws Exception { 
  28.         new SelectSockets().go(args); 
  29.     } 
  30.     public void go(String[] args) throws Exception{ 
  31.         int port = PORT_NUMBER; 
  32. //      if(args.length > 0){ 
  33. //          port = Integer.parseInt(args[0]); 
  34. //      } 
  35. //      System.out.println("Listening on port " + port); 
  36.         ServerSocketChannel serverChannel = ServerSocketChannel.open(); 
  37.         ServerSocket serverSocket = serverChannel.socket(); 
  38.          
  39.         Selector selector = Selector.open(); 
  40.         serverSocket.bind(new InetSocketAddress(port)); 
  41.         serverChannel.configureBlocking(false); 
  42.         serverChannel.register(selector, SelectionKey.OP_ACCEPT); 
  43.          
  44.         while(true){ 
  45.             int n = selector.select(); //没有轮询,单个selector 
  46.             if(n == 0){ 
  47.                 continue;  
  48.             } 
  49.             Iterator it = selector.selectedKeys().iterator(); 
  50.              
  51.             while(it.hasNext()){ 
  52.                 SelectionKey key = (SelectionKey)it.next(); 
  53.                 if(key.isAcceptable()){ 
  54.                     ServerSocketChannel server =                (ServerSocketChannel)key.channel(); 
  55.                     SocketChannel channel = server.accept();        registerChannel(selector,channel,SelectionKey.OP_READ); 
  56.                     sayHello(channel); 
  57.                 } 
  58.                 if(key.isReadable()){ 
  59.                     readDataFromSocket(key); 
  60.                 } 
  61.                 it.remove(); 
  62.             } 
  63.         } 
  64.     } 
  65.     /** 
  66.      * 在selector上注册channel,并设置interest 
  67.      *  
  68.      * Author: chenkangxian 
  69.      * 
  70.      * Last Modification Time: 2011-7-11 
  71.      * 
  72.      * @param selector 选择器 
  73.      *  
  74.      * @param channel 通道 
  75.      *  
  76.      * @param ops interest 
  77.      *  
  78.      * @throws Exception 
  79.      */ 
  80.     protected void registerChannel(Selector selector, 
  81.             SelectableChannel channel, int ops) throws Exception{ 
  82.         if(channel == null){ 
  83.             return ;  
  84.         } 
  85.         channel.configureBlocking(false); 
  86.         channel.register(selector, ops); 
  87.     } 
  88.     /** 
  89.      * 处理有可用数据的通道 
  90.      *  
  91.      * Author: chenkangxian 
  92.      * 
  93.      * Last Modification Time: 2011-7-11 
  94.      * 
  95.      * @param key 可用通道对应的key 
  96.      *  
  97.      * @throws Exception 
  98.      */ 
  99.     protected void readDataFromSocket(SelectionKey key) throws Exception{ 
  100.         SocketChannel socketChannel = (SocketChannel)key.channel(); 
  101.         int count; 
  102.         buffer.clear(); //Empty buffer 
  103.         while((count = socketChannel.read(buffer)) > 0){ 
  104.             buffer.flip();  
  105.             while(buffer.hasRemaining()){ 
  106.                 socketChannel.write(buffer); 
  107.             } 
  108.             buffer.clear();  
  109.         } 
  110.         if(count < 0){ 
  111.             socketChannel.close(); 
  112.         } 
  113.     } 
  114.     /** 
  115.      * 打招呼 
  116.      *  
  117.      * Author: chenkangxian 
  118.      * 
  119.      * Last Modification Time: 2011-7-11 
  120.      * 
  121.      * @param channel 客户端channel 
  122.      *  
  123.      * @throws Exception 
  124.      */ 
  125.     private void sayHello(SocketChannel channel) throws Exception{ 
  126.         buffer.clear(); 
  127.         buffer.put("Hello 哈罗! \r\n".getBytes()); 
  128.         buffer.flip(); 
  129.         channel.write(buffer); 
  130.     } 

原文链接:http://chenkangxian.iteye.com/blog/1288246

【编辑推荐】

  1. Java代码规范那些事
  2. Java效率真的很低吗?Android为何要采用?
  3. 漫谈Java开源5年:自由但带着枷锁
  4. Google提供JavaScript库以简化Google API的调用
  5. 解析Java语言11个主要特性
责任编辑:林师授 来源: chenkangxian的博客
相关推荐

2011-11-25 09:56:16

H3C

2015-11-09 19:03:04

戴尔云计算

2011-12-07 17:17:02

JavaNIO

2015-11-09 17:28:12

戴尔云计算

2022-12-08 09:10:11

I/O模型Java

2012-05-16 17:22:11

Java设计模式

2011-11-17 16:03:05

Java工厂模式Clojure

2012-05-16 17:15:04

Java设计模式

2011-12-07 14:57:44

JavaNIO

2011-12-15 09:55:47

javanio

2011-12-15 11:19:08

JavaNIO

2011-12-15 09:40:06

Javanio

2020-09-10 13:51:48

Kubernetes云原生容器

2023-07-28 08:23:05

选择器Java NIO

2011-04-06 11:41:25

Java动态代理

2013-05-23 15:59:00

线程池

2011-12-07 14:41:51

JavaNIO

2011-12-07 16:12:29

JavaNIO

2015-09-25 09:14:50

java缓冲技术

2011-12-15 10:10:33

Javanio
点赞
收藏

51CTO技术栈公众号