美文网首页
NIO-通道Channel

NIO-通道Channel

作者: 砌月东谷 | 来源:发表于2021-06-30 08:01 被阅读0次

NIO中的Channel(通道)是一个接口,有以下几种实现类:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel等。

可以使用以下方法获取Channel对象

FileInputStream

FileOutStream

RandomAccessFile

这三个属于本地IO,这些类中毒提供了FileChannel getChannel()方法,可以用于获取FileChannel对象

Socket

ServerSocket

DatagramSocket

这三个属于网络型IO类,这些类提供了XXXChannel getChannel()方法,可以用于获取不同类型的Channel对象

FileChannel等各个Channel实现类

Channel实现类中,都提供了用于获取Channel的open()方法

范例1:文件复制,在非直接缓冲区中,借助Channel实现文件复制

public static void main(String[] args) throws IOException {
        long start=System.currentTimeMillis();

        FileInputStream input=new FileInputStream("C:\\Users\\peng\\Desktop\\cdbf6c81800a19d86524639732fa828ba61e4679.jpg");

        FileOutputStream out=new FileOutputStream("C:\\Users\\peng\\Desktop\\1.jpg");

        FileChannel channel = input.getChannel();
        FileChannel channel1 = out.getChannel();

//        ByteBuffer buffer=ByteBuffer.allocateDirect(1024);//直接缓冲区
        ByteBuffer buffer=ByteBuffer.allocate(1024);//非直接缓冲区
        while(channel.read(buffer)!=-1){
            buffer.flip();
            channel1.write(buffer);
            buffer.clear();
        }
        long end =System.currentTimeMillis();

        System.out.println("复制文件用时(毫秒)"+(end-start));
        //依次关闭

        channel1.close();
        channel.close();
        out.close();
        input.close();


    }

allocateDirect在源码中实际调用的是new DirectByteBuffer(capacity)来创建直接缓冲区对象,DirectByteBuffer标识堆外内存,也就是程序中使用的直接缓冲区

上文提到Buffer有一个long address属性,address就是Buffer使用的堆外内存的地址,并且address只会在直接缓冲区中被使用。

DirectByteBuffer类的类定义class DirectByteBuffer extends MappedByteBUffer...,由此可知MappedByteBUffer是堆外内存的父类,因此可以使用MappedByteBuffer来操作堆外内存,即操作直接缓冲区,MappedByteBuffer也成为内存映射文件

在支持零拷贝的操作系统上,可以使用MappedByteBuffer类和FileChannel中的transferFrom()/transferTo()方法进行文件的零拷贝。

范例2:直接缓冲区的文件复制

public void test2() throws IOException{
        long start=System.currentTimeMillis();
        //文件的输入通道
        FileChannel in = FileChannel.open(Paths.get("C:\\Users\\peng\\Desktop\\cdbf6c81800a19d86524639732fa828ba61e4679.jpg"), StandardOpenOption.READ);

        //文件的输出通道
        FileChannel out = FileChannel.open(Paths.get("C:\\Users\\peng\\Desktop\\1.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE);

        //输入通道和输出通道之间的内存映射文件
        MappedByteBuffer inmap = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());

        MappedByteBuffer outmap = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());


        byte[] bytes = new byte[inmap.limit()];

        inmap.get(bytes);
        outmap.put(bytes);

        in.close();
        out.close();
        long end =System.currentTimeMillis();
        System.out.println("复制文件用时(毫秒)"+(end-start));
    }

直接缓冲区中的内存映射文件MappedByteBuffer本身代表了磁盘上对应的物理文件,如果修改了MapperByteBuffer,磁盘上的物理文件也会随之修改(实际上,这种同步操作是由操作系统完成的),因此在开发的时候,应用程序只需要关心内存中的数据。建议将直接缓冲区分配给那些持久的,经常重用的数据使用。

范例3:零拷贝

public void test3() throws IOException{
        long start=System.currentTimeMillis();
        //文件的输入通道
        FileChannel in = FileChannel.open(Paths.get("C:\\Users\\peng\\Desktop\\cdbf6c81800a19d86524639732fa828ba61e4679.jpg"), StandardOpenOption.READ);

        //文件的输出通道
        FileChannel out = FileChannel.open(Paths.get("C:\\Users\\peng\\Desktop\\1.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ,StandardOpenOption.CREATE);

        //输入通道和输出通道之间的内存映射文件

        in.transferTo(0,in.size(),out);



        in.close();
        out.close();
        long end =System.currentTimeMillis();
        System.out.println("复制文件用时(毫秒)"+(end-start));
    }

范例4:资源加锁

在FileChannel中也提供了Lock的加锁方式

public static void main(String[] args) throws IOException, InterruptedException {
        RandomAccessFile rw = new RandomAccessFile("C:\\Users\\peng\\Desktop\\cdbf6c81800a19d86524639732fa828ba61e4679.jpg", "rw");

        FileChannel channel = rw.getChannel();
        //第三个参数是否是共享锁
        FileLock lock = channel.lock(2, 4, false);
        System.out.println("主线程将文件锁定3S");

        new Thread(()->{
            byte[] bs=new byte[8];
            try {
//                rw.read(bs);
                if(lock.isValid()){
                    rw.write("ccc".getBytes(),0,8);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        Thread.sleep(3000);
        System.out.println("3s结束");
        lock.release();
    }

范例5 管道pipe

两个线程之间单项传递数据时,可以使用Pipe规范数据的读写操作

可以使用Pipe.open()创建管道

想向管道写数据可以访问SinkChannel管道,读数据访问SourceChannel管道

public static void testPipe() throws IOException{
 //创建管道
 Pipe pipe = Pipe.open();
 ByteBuffer buf = ByteBuffer.allocate(1024);
 //通过SinkChannel,向Pipe中写数据
 Pipe.SinkChannel  sinkChannel = pipe.sink();
 buf.put("helloworld".getBytes());
 buf.flip();
 sinkChannel.write(buf);
 // 通过SourceChannel,从Pipe中读取数据
 Pipe.SourceChannel sourceChannel = pipe.source();
 buf.flip();
 int len = sourceChannel.read(buf);
 System.out.println( new String(buf.array(),0,len));
 sourceChannel.close();
 sinkChannel.close();
 }

相关文章

  • NIO-通道Channel

    NIO中的Channel(通道)是一个接口,有以下几种实现类:FileChannel、SocketChannel、...

  • Java NIO Channel

    Channel(通道) 定义 Channel(通道)定义在java.nio.channels包中。Channel表...

  • 8.2 架构 - 通道

    通道 (Channel) Hyperledger Fabric 通道 (channel) 是两个或多个特定网络成员...

  • Java NIO(4) - 通道(1)

    3.6 通道(channel)简介 通道(Channel):由 java.nio.channels 包定的。Cha...

  • Java NIO——通道Channel

    Channel 通道的简介 通道( Channel):由 java.nio.channels 包定义的。 Chan...

  • netty优雅关闭channel通道

    channel != null 通道不能为空!channel.isActive() 通道不能是活跃状态的!chan...

  • 通道channel

    channel特点 chan类型的值本身的并发安全的,因此可用于多个goroutine之间通信 chan使用mak...

  • Channel通道

    Channel——通道 Split——分离 Combine——结合 Flip——翻转 Swizzle——调配 调配...

  • Java NIO(2) - 缓冲区(Buffer)

    3. 缓冲区(Buffer)和通道(Channel) Java NIO系统的核心在于:通道(Channel)和缓冲...

  • golang 单向 channel

    只写通道:chan<- T只读通道:<-chan T 即只可写入或只可读的channel,事实上 channel ...

网友评论

      本文标题:NIO-通道Channel

      本文链接:https://www.haomeiwen.com/subject/jpblultx.html