Java资料整理Java学习笔记Java 杂谈

JAVA NIO

2017-04-30  本文已影响72人  卡路fly

NIO是非阻塞的IO,Java NIO由一下几个核心部分组成:<code>Buffers</code>、<code>Channels</code>、<code>Selectors</code>。

Java NIO中有很多类和组件,但是<code>Buffers</code>、<code>Channels</code>、<code>Selectors</code>构成了核心的API。其他组件如<code>Pipe</code>和<code>FileLock</code>,只不过是与其他三个核心组件共同使用的工具类。

  • Channel:基本上所有的IO在NIO中都从一个<code>Channel</code>开始,Channel有点像流。数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。Channel和Buffer有好多类型,Channel主要有:<code>FileChannel、DataGramChannel、SocketChannel、ServerSocketChannel</code>。涵盖了UDP和TCP网络的IO以及文件IO。

缓冲区&Buffer

在整个Java新IO中,所有的操作都是以缓冲区进行的,使用缓冲区,则操作的性能将是最高的。

在Buffer中存在一系列的状态变量,随着写入或读取都有可能被改变,在缓冲区可以使用三个值表示缓冲区状态

ByteBuffer的使用

  1. 创建ByteBuffer

(1)使用allocate()静态方法<code>ByteBuffer buffer=ByteBuffer.allocate(256);</code>
以上方法将创建一个容量为256字节的ByteBuffer,如果发现创建的缓冲区容量太小,唯一的选择就是重新创建一个大小合适的缓冲区.

(2)通过包装一个已有的数组来创建
如下,通过包装的方法创建的缓冲区保留了被包装数组内保存的数据.
<code>ByteBuffer buffer=ByteBuffer.wrap(byteArray);</code>
如果要将一个字符串存入ByteBuffer,可以如下操作:
<code>String sendString="你好,服务器. ";
ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-16"));</code>

  1. 缓冲区

这个方法用来将缓冲区准备为数据传出状态,执行以上方法后,输出通道会从数据的开头而不是末尾开始.回绕保持缓冲区中的数据不变,只是准备写入而不是读取.

这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值.不必为了每次读写都创建新的缓冲区,那样做会降低性能.相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区.

ByteBuffer转为其他的Buffer,如:CharBuffer、DoubleBuffer、IntBuffer、LongBuffer、ShortBuffer,都有对应的asXXXBuffer()方法。

import java.nio.IntBuffer ;
public class IntBufferDemo02{
public static void main(String args[]){
IntBuffer buf = IntBuffer.allocate(10) ; // 准备出10个大小的缓冲区
IntBuffer sub = null ; // 定义子缓冲区
for(int i=0;i<10;i++){
buf.put(2 * i + 1) ; // 在主缓冲区中加入10个奇数
}

    // 需要通过slice() 创建子缓冲区
    buf.position(2) ;
    buf.limit(6) ;
    sub = buf.slice() ;
    for(int i=0;i<sub.capacity();i++){
        int temp = sub.get(i) ;
        sub.put(temp-1) ;
    }

    buf.flip() ;    // 重设缓冲区
    buf.limit(buf.capacity()) ;
    System.out.print("主缓冲区中的内容:") ;
    while(buf.hasRemaining()){
        int x = buf.get() ;
        System.out.print(x + "、") ;
    }
}

}


- 创建只读缓冲区 buf.asReadOnlyBuffer()
```java
import java.nio.IntBuffer ;
public class IntBufferDemo03{
  public static void main(String args[]){
      IntBuffer buf = IntBuffer.allocate(10) ;    // 准备出10个大小的缓冲区
      IntBuffer read = null ; // 定义子缓冲区
      for(int i=0;i<10;i++){
          buf.put(2 * i + 1) ;    // 在主缓冲区中加入10个奇数
      }
      read = buf.asReadOnlyBuffer()  ;// 创建只读缓冲区
      
      read.flip() ;   // 重设缓冲区
      System.out.print("主缓冲区中的内容:") ;
      while(read.hasRemaining()){
          int x = read.get() ;
          System.out.print(x + "、") ;
      }
      read.put(30) ;  // 修改,错误
  }
}
import java.nio.ByteBuffer ;
public class ByteBufferDemo01{
    public static void main(String args[]){
        ByteBuffer buf = ByteBuffer.allocateDirect(10) ;    // 准备出10个大小的缓冲区
        byte temp[] = {1,3,5,7,9} ; // 设置内容
        buf.put(temp) ; // 设置一组内容
        buf.flip() ;

        System.out.print("主缓冲区中的内容:") ;
        while(buf.hasRemaining()){
            int x = buf.get() ;
            System.out.print(x + "、") ;
        }
    }
}

通道

读入方式

FileChannel的三种内存映射模式

|NO. | 常量|类型|描述|
| ------------- |------------|------------|-------------|---|
|1|public static final FileChannel.MapMode.READ_ONLY|常量|只读映射模式|
|2|public static final FileChannel.MapMode.READ_WRITE|常量|读取/写入映射模式|
|3|public static final FileChannel.MapMode.PRIVATE |常量|专用(写入时拷贝)映射模式|

import java.nio.ByteBuffer ;
import java.nio.MappedByteBuffer ;
import java.nio.channels.FileChannel ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.FileInputStream ;
public class FileChannelDemo03{
    public static void main(String args[]) throws Exception{
        File file = new File("d:" + File.separator + "mldn.txt") ;  
        FileInputStream input = null ;
        input = new FileInputStream(file) ;
        FileChannel fin = null ;    // 定义输入的通道
        fin = input.getChannel() ;  // 得到输入的通道
        MappedByteBuffer mbb = null ; 
        mbb = fin.map(FileChannel.MapMode.READ_ONLY,0,file.length()) ;
        byte data[] = new byte[(int)file.length()] ;    // 开辟空间接收内容
        int foot = 0 ;
        while(mbb.hasRemaining()){
            data[foot++] = mbb.get() ;  // 读取数据
        }
        System.out.println(new String(data)) ;  // 输出内容
        fin.close() ;
        input.close() ;
    }
}

内存映射在读取时速度快,但是如果在使用以上操作代码的时候,执行的是写入操作则有可能非常危险,因为仅仅是改变数组中单个元素这种简单的操作,就可能直接修改磁盘上的文件,因为修改数据与将数据保存在磁盘上是一样的。


文件锁FileLock

当一个线程将文件锁定之后,其它线程无法操作此文件,要想进行文件锁定操作,需要使用<code>FileLock</code>类完成,此类的对象需要依靠<code>FileChannel</code>进行实例化操作。

实例化<code>FileLock</code>对象的方法

锁定方式


字符集 Charset

整个NIO中,对于不同平台的编码操作,java都可以进行自动适应,因为可以使用字符集进行字符编码的转换。

在java中,所有信息都是以UNICODE进行编码,计算机中存在多种编码,NIO中提供了charset类来处理编码问题,包括了创建编码器(<code>CharsetEndoder</code>)和创建解码器(<code>CharsetDecoder</code>)的操作。

得到所有字符集
        Charset latin1 = Charset.forName("ISO-8859-1") ;    // 只能表示的英文字符
        CharsetEncoder encoder = latin1.newEncoder() ;  // 得到编码器
        CharsetDecoder decoder = latin1.newDecoder() ;  // 得到解码器
        // 通过CharBuffer类中的
        CharBuffer cb = CharBuffer.wrap("啊哈哈哈哈") ;
        ByteBuffer buf = encoder.encode(cb) ;   // 进行编码操作
        System.out.println(decoder.decode(buf)) ;   // 进行解码操作

Selector

解决服务器端的通讯性能。

import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Set;
import java.util.Iterator;
import java.util.Date;
import java.nio.channels.ServerSocketChannel;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;

public class DateServer {
    public static void main(String args[]) throws Exception {
        int ports[] = { 8000, 8001, 8002, 8003, 8005, 8006 }; // 表示五个监听端口
        Selector selector = Selector.open(); // 通过open()方法找到Selector
        for (int i = 0; i < ports.length; i++) {
            ServerSocketChannel initSer = null;
            initSer = ServerSocketChannel.open(); // 打开服务器的通道
            initSer.configureBlocking(false); // 服务器配置为非阻塞
            ServerSocket initSock = initSer.socket();
            InetSocketAddress address = null;
            address = new InetSocketAddress(ports[i]); // 实例化绑定地址
            initSock.bind(address); // 进行服务的绑定
            initSer.register(selector, SelectionKey.OP_ACCEPT); // 等待连接
            System.out.println("服务器运行,在" + ports[i] + "端口监听。");
        }
        // 要接收全部生成的key,并通过连接进行判断是否获取客户端的输出
        int keysAdd = 0;
        while ((keysAdd = selector.select()) > 0) { // 选择一组键,并且相应的通道已经准备就绪
            Set<SelectionKey> selectedKeys = selector.selectedKeys();// 取出全部生成的key
            Iterator<SelectionKey> iter = selectedKeys.iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next(); // 取出每一个key
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel client = server.accept(); // 接收新连接
                    client.configureBlocking(false);// 配置为非阻塞
                    ByteBuffer outBuf = ByteBuffer.allocateDirect(1024); //
                    outBuf.put(("当前的时间为:" + new Date()).getBytes()); // 向缓冲区中设置内容
                    outBuf.flip();
                    client.write(outBuf); // 输出内容
                    client.close(); // 关闭
                }
            }
            selectedKeys.clear(); // 清楚全部的key
        }

    }
}

上一篇 下一篇

猜你喜欢

热点阅读