首页 > 其他分享 >2.channel

2.channel

时间:2024-11-01 21:49:16浏览次数:3  
标签:文件 buffer throws IOException public channel

传统的io中,数据通过流传输;在nio中,数据放在缓冲区中进行管理,通过通道进行传输

1.通道接口层次

1.1相关接口介绍

根基接口Channel

public interface Channel extends Closeable {
    //通道是否处于开启状态
    public boolean isOpen();

    //因为通道开启也需要关闭,所以实现了Closeable接口,所以这个方法懂的都懂
    public void close() throws IOException;
}

定义读写操作的接口

public interface ReadableByteChannel extends Channel {
    //将通道中的数据读取到给定的缓冲区中
    public int read(ByteBuffer dst) throws IOException;
}

public interface WritableByteChannel extends Channel {
  	//将给定缓冲区中的数据写入到通道中
    public int write(ByteBuffer src) throws IOException;
}

读写的整合

public interface ByteChannel extends ReadableByteChannel, WritableByteChannel{

}

ByteChannel下的衍生接口

//允许保留position和更改position的通道,以及对通道连接实体的相关操作
public interface SeekableByteChannel extends ByteChannel {
   	...

    //获取当前的position
    long position() throws IOException;

    //修改当前的position
    SeekableByteChannel position(long newPosition) throws IOException;

    //返回此通道连接到的实体(比如文件)的当前大小
    long size() throws IOException;

    //将此通道连接到的实体截断(比如文件,截断之后,文件后面一半就没了)为给定大小
    SeekableByteChannel truncate(long size) throws IOException;
}

响应中断的接口

public interface InterruptibleChannel extends Channel {
  	//当其他线程调用此方法时,在此通道上处于阻塞状态的线程会直接抛出 AsynchronousCloseException 异常
    public void close() throws IOException;
}

//这是InterruptibleChannel的抽象实现,完成了一部分功能
public abstract class AbstractInterruptibleChannel implements Channel, InterruptibleChannel {
		//加锁关闭操作用到
    private final Object closeLock = new Object();
  	//当前Channel的开启状态
    private volatile boolean open = true;

    protected AbstractInterruptibleChannel() { }

    //关闭操作实现
    public final void close() throws IOException {
        synchronized (closeLock) {   //同时只能有一个线程进行此操作,加锁
            if (!open)   //如果已经关闭了,那么就不用继续了
                return;
            open = false;   //开启状态变成false
            implCloseChannel();   //开始关闭通道
        }
    }

    //该方法由 close 方法调用,以执行关闭通道的具体操作,仅当通道尚未关闭时才调用此方法,不会多次调用。
    protected abstract void implCloseChannel() throws IOException;

    public final boolean isOpen() {
        return open;
    }

    //开始阻塞(有可能一直阻塞下去)操作之前,需要调用此方法进行标记,
    protected final void begin() {
        ...
    }

  	//阻塞操作结束之后,也需要需要调用此方法,为了防止异常情况导致此方法没有被调用,建议放在finally中
    protected final void end(boolean completed)
				...
    }
		
		...
}

1.2 使用通道读取数据

public static void main(String[] args) throws IOException {
  	//缓冲区创建好,一会就靠它来传输数据
    ByteBuffer buffer = ByteBuffer.allocate(10);
    //将System.in作为输入源,一会Channel就可以从这里读取数据,然后通过缓冲区装载一次性传递数据
    ReadableByteChannel readChannel = Channels.newChannel(System.in);
    while (true) {
        //将通道中的数据写到缓冲区中,缓冲区最多一次装10个
        readChannel.read(buffer);
        //写入操作结束之后,需要进行翻转,以便接下来的读取操作
        buffer.flip();
        //最后转换成String打印出来康康
        System.out.println("读取到一批数据:"+new String(buffer.array(), 0, buffer.remaining()));
        //回到最开始的状态
        buffer.clear();
    }
}

注:Channel不像流那样是单向的,它就像它的名字一样,一个通道可以从一端走到另一端,也可以从另一端走到这一端

2.文件传输

相比传统的文件输入输出,需要至少两个流,一个完成读操作一个完成写操作,但是channel通过buffer就可以轻易实现读写转换,从而只需要一个类来完成

public static void main(String[] args) throws IOException {
    /*
      通过RandomAccessFile进行创建,注意后面的mode有几种:
      r        以只读的方式使用
      rw   读操作和写操作都可以
      rws  每当进行写操作,同步的刷新到磁盘,刷新内容和元数据
      rwd  每当进行写操作,同步的刷新到磁盘,刷新内容
     */
    try(RandomAccessFile f = new RandomAccessFile("test.txt", "rw");  //这里设定为支持读写,这样创建的通道才能具有这些功能
        FileChannel channel = f.getChannel()){   //通过RandomAccessFile创建一个通道
        channel.write(ByteBuffer.wrap("伞兵二号马飞飞准备就绪!".getBytes()));

        System.out.println("写操作完成之后文件访问位置:"+channel.position());  //注意读取也是从现在的位置开始
        channel.position(0);  //需要将位置变回到最前面,这样下面才能从文件的最开始进行读取

        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);
        buffer.flip();

        System.out.println(new String(buffer.array(), 0, buffer.remaining()));
    }
}

注:通过FileInputStream和FileOutputStream获取的channel也只能完成一种操作

对文件进行截断

public static void main(String[] args) throws IOException {
    try(RandomAccessFile f = new RandomAccessFile("test.txt", "rw");
        FileChannel channel = f.getChannel()){
        //截断文件,只留前20个字节
        channel.truncate(20);

        ByteBuffer buffer = ByteBuffer.allocate(128);
        channel.read(buffer);
        buffer.flip();
        System.out.println(new String(buffer.array(), 0, buffer.remaining()));
    }
}

文件复制

public static void main(String[] args) throws IOException {
    try(FileOutputStream out = new FileOutputStream("test2.txt");
        FileInputStream in = new FileInputStream("test.txt")){

        FileChannel inChannel = in.getChannel();   //获取到test文件的通道
        inChannel.transferTo(0, inChannel.size(), out.getChannel());   //直接将test文件通道中的数据转到test2文件的通道中
        // 或者反向可以使用transferFrom
    }
}

文件编辑

此处使用的就是DirectByteBuffer直接缓冲区,效率还是很高的。

//注意一定要是可写的,不然无法进行修改操作
try(RandomAccessFile f = new RandomAccessFile("test.txt", "rw");
    FileChannel channel = f.getChannel()){

    //通过map方法映射文件的某一段内容,创建MappedByteBuffer对象
    //比如这里就是从第四个字节开始,映射10字节内容到内存中
  	//注意这里需要使用MapMode.READ_WRITE模式,其他模式无法保存数据到文件
    MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 4, 10);

    //我们可以直接对在内存中的数据进行编辑,也就是编辑Buffer中的内容
  	//注意这里写入也是从pos位置开始的,默认是从0开始,相对于文件就是从第四个字节开始写
  	//注意我们只映射了10个字节,也就是写的内容不能超出10字节了
    buffer.put("yyds".getBytes());

    //编辑完成后,通过force方法将数据写回文件的映射区域
    buffer.force();
}

3.文件锁

  • 我们可以创建一个跨进程文件锁来防止多个进程之间的文件争抢操作(注意这里是进程,不是线程)FileLock是文件锁,它能保证同一时间只有一个进程(程序)能够修改它,或者都只可以读,这样就解决了多进程间的同步文件,保证了安全性。但是需要注意的是,它进程级别的,不是线程级别的,他可以解决多个进程并发访问同一个文件的问题,但是它不适用于控制同一个进程中多个线程对一个文件的访问

  • 加锁操作

    • public static void main(String[] args) throws IOException, InterruptedException {
        	//创建RandomAccessFile对象,并拿到Channel
          RandomAccessFile f = new RandomAccessFile("test.txt", "rw");
          FileChannel channel = f.getChannel();
          System.out.println(new Date() + " 正在尝试获取文件锁...");
        	//接着我们直接使用lock方法进行加锁操作(如果其他进程已经加锁,那么会一直阻塞在这里)
        	//加锁操作支持对文件的某一段进行加锁,比如这里就是从0开始后的6个字节加锁,false代表这是一把独占锁 true代表是共享锁
        	//范围锁甚至可以提前加到一个还未写入的位置上
          FileLock lock = channel.lock(0, 6, false);
          System.out.println(new Date() + " 已获取到文件锁!");
          Thread.sleep(5000);   //假设要处理5秒钟
          System.out.println(new Date() + " 操作完毕,释放文件锁!");
        	
        	//操作完成之后使用release方法进行锁释放
          lock.release();
      }
      
  • 有关共享锁和独占锁:

    • 进程对文件加独占锁后,当前进程对文件可读可写,独占此文件,其它进程是不能读该文件进行读写操作的。
    • 进程对文件加共享锁后,进程可以对文件进行读操作,但是无法进行写操作,共享锁可以被多个进程添加,但是只要存在共享锁,就不能添加独占锁。
  • 非阻塞的方式加锁tryLock(),如果失败则返回null

标签:文件,buffer,throws,IOException,public,channel
From: https://www.cnblogs.com/yuqiu2004/p/18521354

相关文章

  • Go 语言的Channel
    在Go语言中,Channel是一种用于在多个Goroutine之间传递数据的通信机制。Channel提供了类型安全、同步的数据传输方式,使Goroutine可以相互通信而无需使用锁。1.Channel的定义与声明在Go中,可以使用make函数创建Channel,并指定Channel中传输的数据类型:ch:=make(......
  • 『QEmu』使用 QIOChannel 进行 unix socket 通信
    在QEmu中使用常规的read(...)、recv(...)或者write(...)、send(...)进行堵塞式IO读写有时候会无法得到预期的结果,这是因为QEmu使用基于glib事件循环的事件循环,所有的读写操作都应该统一在QEmu的框架中进行。QEmu的内部API较为复杂,存在多种不同封装级别的IO读写......
  • Go批量读取channel的数据
    packagemainimport("fmt""time")funcbatchProcessor(ch<-chanstring,batchSizeint,flushIntervaltime.Duration){varbatch[]stringtimer:=time.NewTimer(flushInterval)for{......
  • HCI_LE_Read_Advertising_Channel_Tx_Power(0x0007)命令全面解析
    目录一、命令概述二、命令格式2.1.HCI_LE_Read_Advertising_Channel_Tx_Power命令一般格式2.2.示例格式2.2.1.命令示例2.2.2.响应示例 三、返回参数说明3.1.状态码(Status)3.2.传输功率等级(Advertising_Channel_Tx_Power_Level)四、命令执行流程4.1.命令准备......
  • Cinemachine系列——Noise&Basic Multi Channel Perlin
    在Cinemachine相机的游戏对象中使用基本多通道柏林噪声组件,以通过柏林噪声运动模拟相机抖动。柏林噪声是一种计算伪随机运动并具有自然行为的技术。简单来说,基本多通道柏林噪声组件应用了一个噪声配置资产,用于定义噪声随时间变化的行为。Cinemachine自带了一些噪声配置资产,你可以......
  • Chapter 2 - 1. Understanding Congestion in Fibre Channel Fabrics
    Thischaptercoversthefollowingtopics:本章包括以下主题: FibreChannelFlowControl.光纤通道流量控制 CongestionSpreadinginFibreChannelFabrics.光纤通道Fabric中的拥塞扩散 FrameFlowwithinaFibreChannelSwitch.光纤通道交换机内的帧流......
  • springintegration handle message with messagid between three channels
    InSpringIntegration,handlingamessageacrossmultiplechannelswhilepreservingamessageId(orsimilaridentifier)canbeachievedbyleveragingmessagerouting,channels,andcustommessageheaders.Here’showyoucanrouteandprocessmessagesbetw......
  • NetCore Channel-生产者&消费者
    usingSystem.Threading.Channels;namespaceChannelDemo{publicclassChannelMgr{//优势//允许开发者根据需要创建具有固定容量(有界)或无限容量(无界)的通道//staticChannel<string>channel=Channel.CreateBounded<strin......
  • Go语言并发编程之Channels详解
    并发编程是Go语言的一大特色,而channel(通道)则是Go语言中用于实现并发的核心工具之一。它源于CSP(CommunicatingSequentialProcesses)的概念,旨在让多个goroutine之间能够高效地进行通信和同步。本文将深入探讨channel的用法、原理和最佳实践,通过丰富的示例代码和详细的解释,帮......
  • 织梦如何让channelartlist标签支持limit属性
    在织梦CMS(DEDECMS)中,默认情况下channelartlist标签并不支持limit属性。但是,你可以通过修改织梦CMS的核心文件来实现这一功能。以下是详细的步骤:步骤1:备份现有文件在进行任何修改之前,请确保备份相关文件,以防修改失败或出现其他问题。步骤2:修改核心文件定位文件:打......