java 网络学习 之nio模型基础介绍channel(6)

通道(Channel)

通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。Channel 负责传输, Buffer 负责存储。通道是由 java.nio.channels 包定义的。 Channel 表示 IO 源与目标打开的连接。Channel 类似于传统的“流”。只不过 Channel本身不能直接访问数据, Channel 只能与Buffer 进行交互。

通道都是操作缓存区完成全部的功能的。

Java中所有已知 Channel 实现类:

 

wKioL1dDHruTa0LrAAJMdjtHDkQ398.png

最下面的4个类为具体实现类。共包括7个接口类、3个抽象类和4个实现类

  • Channel是最顶层的接口。

  • ReadableByteChannel和WritableByteChannel分别提供对通道读取和写入Byte数据的功能

  • ByteChannel用来将读取和写入的功能合并

  • ScatteringByteChannel和GatheringByteChannel分别提供了批量读取和写入ByteBuffer数组的能力

  • InterruptibleChannel提供了多线程异步关闭的能力

7个接口类的详细方法如下

  • Channel代表一个可以进行IO操作的通道。定义了一下方法

boolean isOpen();//判断此通道是否处于打开状态
void close();//关闭这个Channel,相关资源被释放
  • ReadableByteChannel和WritableByteChannel分别定义了一个读取和写入byte数据的Channel接口。分别定义了read()和write()方法。

int read(ByteBuffer dst);//将字节序列从此通道中读入到给定的缓存区
int write(ByteBuffer src);//将字节序列从给定的缓存区写入此通道
  • ByteChannel没有定义新的方法,它的作用只是把ReadableByteChannel和WritableByteChannel合并在一起

  • ScatteringByteChannel可以一次将数据从通道读入多个ByteBuffer中。而GatheringByteBufferChannel可以一次将多个ByteBuffer中的数据写入通道。提供的方法如下

long read(ByteBuffer[] dsts);//将字节序列从此通道中读入给定的缓存区
long read(ByteBuffer[],int offset,int length);//讲字节序列从此通道中读入给定的缓存区,位置从offset开始到length
long write(ByteBuffer[] src);//将字节序列从给定的缓存区写入此通道
long write(ByteBuffer[] src,int offset,int length);//将字节序列从给定的缓存区写入此通道,位置从offset开始到length
  • InterruptibleChannel用来提供一个可以被异步关闭的Channel,它覆盖了Channel接口的关闭方法close()。

常用的有入下几个:

  • FileChannel:用于读取、写入、映射和操作文件的通道。

  • DatagramChannel:通过 UDP 读写网络中的数据通道。

  • SocketChannel:通过 TCP 读写网络中的数据。

  • ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。

由此可以看出,nio 的实际应用侧重点还是网络数据读写。后续会介绍nio 在socket 中的应用。

获取通道

获取通道的一种方式是对支持通道的对象调用getChannel() 方法。支持通道的类如下:

  • FileInputStream

  • FileOutputStream

  • RandomAccessFile

  • DatagramSocket

  • Socket

  • ServerSocket

获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。或者通过通道的静态方法 open() 打开并返回指定通道。

 

FileChannel

 

1 :FileChannel读写数据

  • 准备以FileOutputStream类为准,这两个类都是支持通道操作的。

  1. String info[] = {"hello","world"} ;

  2. File file = new File("d:" + File.separator + "testfilechannel.txt") ;

  3. FileOutputStream output = null ;

  4. FileChannel fout = null;

  5. try {

  6.    output = new FileOutputStream(file) ;

  7.    fout = null;

  8.    fout = output.getChannel() ;    // 得到输出的通道

  9.    ByteBuffer buf = ByteBuffer.allocate(1024) ;

  10.    for(int i=0;i<info.length;i++){

  11.        buf.put(info[i].getBytes()) ;   // 字符串变为字节数组放进缓冲区之中

  12.    }

  13.    buf.flip() ;

  14.    fout.write(buf) ;   // 输出缓冲区的内容

  15. } catch (Exception e) {

  16.    e.printStackTrace();

  17. }finally{

  18.    if(fout!=null){

  19.        try {

  20.            fout.close() ;

  21.        } catch (IOException e) {

  22.            e.printStackTrace();

  23.        }

  24.    }

  25.    if(output!=null){

  26.        try {

  27.            output.close() ;

  28.        } catch (IOException e) {

  29.            e.printStackTrace();

  30.        }

  31.    }

  32. }

 

2 文件锁(FileLock) (基于FileChannel)

文件锁和其他我们了解并发里面的锁很多概念类似,当多个人同时操作一个文件的时候,只有第一个人可以进行编辑,其他要么关闭(等第一个人操作完成之后可以操作),要么以只读的方式进行打开。

在java nio中提供了新的锁文件功能,当一个线程将文件锁定之后,其他线程无法操作此文件,文件的锁操作是使用FileLock类来进行完成的,此类对象需要依赖FileChannel进行实例化。

文件锁方式

  • 共享锁:允许多个线程进行文件读取。

  • 独占锁:只允许一个线程进行文件的读写操作。

Java文件依赖FileChannel的主要涉及如下4个方法:

方法说明
lock()获取对此通道的文件的独占锁定。
lock(long position, long size, boolean shared)获取此通道的文件给定区域上的锁定。
tryLock() throws IOException试图获取对此通道的文件的独占锁定。
tryLock(long position, long size, boolean shared) throws IOException试图获取对此通道的文件给定区域的锁定。
lock()等同于lock(0L, Long.MAX_VALUE, false)
tryLock()等同于tryLock(0L, Long.MAX_VALUE, false)

 

lock()和tryLock()的区别

  • lock()阻塞的方法,锁定范围可以随着文件的增大而增加。无参lock()默认为独占锁;有参lock(0L, Long.MAX_VALUE, true)为共享锁。

  • tryLock()非阻塞,当未获得锁时,返回null。无参tryLock()默认为独占锁;有参tryLock(0L, Long.MAX_VALUE, true)为共享锁。

简单实例代码:

  1. File file = new File("d:" + File.separator + "test.txt") ;

  2. FileOutputStream output = null ;

  3. FileChannel fout = null ;

  4. try {

  5.    output = new FileOutputStream(file,true) ;

  6.    fout = output.getChannel() ;// 得到通道

  7.    FileLock lock = fout.tryLock() ; // 进行独占锁的操作

  8.    if(lock!=null){

  9.    System.out.println(file.getName() + "文件锁定") ;

  10.    Thread.sleep(5000) ;

  11.    lock.release() ;    // 释放

  12.    System.out.println(file.getName() + "文件解除锁定。") ;

  13.    }

  14. } catch (IOException e) {

  15.    e.printStackTrace();

  16. } catch (InterruptedException e) {

  17.    e.printStackTrace();

  18. } finally {

  19.    if(fout!=null){

  20.    try {

  21.        fout.close();

  22.    } catch (IOException e) {

  23.        e.printStackTrace();

  24.    }

  25.    }

  26.    if(output!=null){

  27.    try {

  28.        output.close();

  29.    } catch (IOException e) {

  30.        e.printStackTrace();

  31.    }

  32.    }

  33. }

3 MappedByteBuffer 内存映射 基于FileChannel

MappedByteBuffer是通过FileChannel创建的文件到内存的映射,是一个直接缓存区。

try {
			File file = new File("D:\\DownloadFile\\test1.txt");
			if(!file.exists()){
				if(file.createNewFile()){
					System.out.println("文件创建成功");
				}
			}
			//使用内存映射复制文件
			FileChannel channel = new FileInputStream("D:\\DownloadFile\\test.txt").getChannel();
			FileChannel channel2 = new FileOutputStream(file).getChannel();
			//映射第一个文件到内存
			MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
			//写入到第二个文件
			channel2.write(buffer);
			channel.close();
			channel2.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

 

 

SelectableChannel

SelectableChannel在打开后,可以使用register()将它注册到特定的选择器,实现通道与选择器的事件绑定。

 //注册到Selector
    SelectableChannel socket = SocketChannle.open();
    socket.configureBlocking(false);//设置为非阻塞模式才可以注册到选择器
    socket.register(selector,SelectionKey.OP_CONNECT);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值