java nio典型示例

NIO基础

Buffer

Buffer工作原理:capacity - 缓冲区容量,缓冲区满了以后,必须清空后才能继续写入数据。position - 写数据到缓冲区时,position代表写入数据的当前位置,初始值为0,当一个字节数据写入到缓冲区后,position会移动到写一个可插入数据的缓冲区单元;当从缓冲区中读取数据时,position表示读取数据的当前位置,调用flip方法切换到读模式时,position会被重置为零。limit - 写数据时表示最多可以写入多少个数据;读数据时,表示还有多少个数据可读取。

//IntBufferDemo.java
import java.nio.IntBuffer;

public class IntBufferDemo {
    public static void main(String[] args) {
    	//每个Buffer类都有一个allocate方法
        IntBuffer buffer = IntBuffer.allocate(8);
        for (int i = 0; i < buffer.capacity(); i++) {
            int j = 2 * (i+1);
            //可以使用通道或者put方法写入数据到缓冲区
            buffer.put(j);
        }
        //clear方法清空缓冲区,compact方法只清除已读取过的数据
        //mark方法可以标记缓冲区中的一个特定position,然后可以通过reset恢复到这个位置
        //rewind方法是把position设置为0,可以重读缓冲区中的数据
        //flip方法把缓冲区从写模式切换到读模式,把limit设置成position的当前值,然后把position清零
        buffer.flip();
        while(buffer.hasRemaining()) {
            //从缓冲区读取数据,可以使用通道读取或者使用get方法
            System.out.println(buffer.get() + ",");
        }
    }
}

//BufferDemo1.java
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class BufferDemo1 {
    public static void main(String[] args) throws Exception {
        RandomAccessFile file = new RandomAccessFile("01.txt", "rw");
        //文件通道从缓冲区读取数据
        FileChannel channel = file.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = channel.read(buffer);
        while(bytesRead != -1){
            buffer.flip();
            while(buffer.hasRemaining()){
                System.out.print((char)buffer.get());
            }
            buffer.clear();
            bytesRead = channel.read(buffer);
        }
        file.close();
    }
}

//BufferDemo2.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class BufferDemo2 {
    public static void main(String[] args) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(10);
        for(int i=0;i<buffer.capacity();i++){
            buffer.put((byte)i);
        }

        //创建子缓冲区
        buffer.position(3);
        buffer.limit(7);
        ByteBuffer sliceBuffer = buffer.slice();
        for(int i=0;i<sliceBuffer.capacity();i++){
            byte b = sliceBuffer.get(i);
            b *= 10;
            sliceBuffer.put(i, b);
        }
        buffer.position(0);
        buffer.limit(buffer.capacity());
        while(buffer.hasRemaining()){
            System.out.println(buffer.get());
        }

        //创建只读缓冲区,只读缓冲区随着原缓冲区发生变化
        IntBuffer intBuffer = IntBuffer.allocate(10);
        for(int i=0;i<intBuffer.capacity();i++){
            intBuffer.put(i);
        }
        IntBuffer readOnlyBuffer = intBuffer.asReadOnlyBuffer();
        for(int i=0;i<intBuffer.capacity();i++){
            int b = intBuffer.get(i);
            b *= 10;
            intBuffer.put(i, b);
        }
        readOnlyBuffer.position(0);
        readOnlyBuffer.limit(intBuffer.capacity());
        while(readOnlyBuffer.hasRemaining()){
            System.out.println(readOnlyBuffer.get());
        }

        //直接缓冲区
        //ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);

        //内存映射文件I/O
        RandomAccessFile file = new RandomAccessFile("02.txt", "rw");
        FileChannel fileChannel = file.getChannel();
        MappedByteBuffer mbb = 
            fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 1024);
        mbb.put(0, (byte)97);
        mbb.put(1023, (byte)122);
        file.close();
    }
}
  • 使用多线程的方式实现
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NioBufferInstance {

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        //获取流的位置
        service.submit(() -> {
            IntBuffer buffer = IntBuffer.allocate(10);
            System.out.println("The capacity of ints buffer:" + buffer.capacity());
            Arrays.asList(0, 1, 2, 3, 4)
                .forEach(p -> buffer.put(new SecureRandom().nextInt(20)));
            System.out.println("before flip limit:" + buffer.limit());
            buffer.flip();
            System.out.println("after flip limit:" + buffer.limit());
            while(buffer.hasRemaining()){
                System.out.println("position:" + buffer.position());
                System.out.println("limit:" + buffer.limit());
                System.out.println("capacity:" + buffer.capacity());
                System.out.println(buffer.get());
            }
        });

        //文件通道读写
        service.submit(() -> {
            try {
                FileOutputStream os = new FileOutputStream("output.txt");
                FileInputStream is = new FileInputStream("input.txt");
                FileChannel inputChannel = is.getChannel();
                FileChannel outputChannel = os.getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(4);
                while(true){
                    buffer.clear();
                    int read = inputChannel.read(buffer);
                    System.out.println("read:" + read);
                    if(read == -1){
                        break;
                    }

                    buffer.flip();
                    outputChannel.write(buffer);
                }

                inputChannel.close();
                outputChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        //随机存取,使用堆外内存
        service.submit(() -> {
            try {
                RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
                FileChannel channel = file.getChannel();
                MappedByteBuffer buffer = 
                    channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
                buffer.put(0, (byte) 'a');
                buffer.put(3, (byte) 'b');
                file.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        //allocate wrap HeapByteBuffer 在JVM堆中创建缓冲区
        //directBuffer DirectByteBuffer 在堆外创建缓冲区,直接与IO设备交互,零拷贝
        service.submit(() -> {
            ByteBuffer buffer = ByteBuffer.allocate(64);
            buffer.putInt(15);
            buffer.putLong(500000000L);
            buffer.putChar('你');
            buffer.putShort((short) 2);
            buffer.putChar('我');
            buffer.flip();
            System.out.println(buffer.getInt() + ","
                               + buffer.getLong() + "," + buffer.getChar()
                    + ',' + buffer.getShort() + "," + buffer.getChar());
            //重置position、limit后重新赋值
            buffer.clear();
            for(int i=0; i<buffer.capacity(); i++){
                buffer.put((byte) i);
            }

            //指定position和limit
            buffer.position(2);
            buffer.limit(6);
            //slice创建一个新的ByteBuffer,新的ByteBuffer操作的结果影响原ByteBuffer
            ByteBuffer sliceBuffer = buffer.slice();
            for(int i=0;i<sliceBuffer.capacity();i++){
                byte b = sliceBuffer.get(i);
                b *= 2;
                sliceBuffer.put(i, b);
            }
            buffer.position(0);
            buffer.limit(buffer.capacity());
            while(buffer.hasRemaining()){
                System.out.print(buffer.get() + "\t");
            }
            System.out.println();
            buffer.clear();
            System.out.println(buffer.getClass());
            ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
            System.out.println(readOnlyBuffer.getClass());
        });

        //通道写
        service.submit(() -> {
            try {
                FileOutputStream fos = new FileOutputStream("NioChannelDemo.txt");
                FileChannel channel = fos.getChannel();
                ByteBuffer buffer = ByteBuffer.allocate(512);
                byte[] message = "Hello world, welcome!".getBytes();
                for(byte b:message){
                    buffer.put(b);
                }
                buffer.flip();
                channel.write(buffer);
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        //通道读
        service.submit(() -> {
            try {
                FileInputStream fis = new FileInputStream("NioChannelDemo.txt");
                FileChannel fileChannel = fis.getChannel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                fileChannel.read(byteBuffer);
                byteBuffer.flip();
                while(byteBuffer.remaining() > 0){
                    byte b = byteBuffer.get();
                    System.out.println("Character:" + (char)b);
                }

                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

}

Channel

SocketChannel是用来连接到TCP网络套接字的通道,主要用来处理网络IO通道,基于TCP传输,实现了可选择通道,可以被多路复用。对于已经存在的套接字不能创建SocketChannel。没有进行连接的SocketChannel执行IO操作时会抛异常。SocketChannel的open方法创建的通道没有进行网络级联,需要使用connect方法连接到指定地址。SocketChannel支持异步关闭、设定参数。ServerSocketChannel是一个基于通道的socket监听器,调用accept方法返回SocketChannel。

//SocketChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class SocketChannelDemo {
    public static void main(String[] args) throws IOException {
        //创建SocketChannel
        SocketChannel socketChannel = 
            SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
        socketChannel.configureBlocking(false);
        ByteBuffer buffer = ByteBuffer.allocate(16);
        socketChannel.read(buffer);
        socketChannel.close();
        System.out.println("read over");
    }
}

//ServerSocketChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class ServerSocketChannelDemo {
    public static void main(String[] args) throws IOException {
        int port = 8888;
        //创建Buffer
        ByteBuffer buffer = ByteBuffer.wrap("hello world".getBytes());
        //打开ServerSocketChannel,绑定端口号
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(port));
        //设置非阻塞模式
        ssc.configureBlocking(false);
        //监听新连接
        for(;;){
            System.out.println("Waiting for connecting...");
            SocketChannel sc = ssc.accept();
            if(sc == null){
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                System.out.println("Incoming connection from:" 
                                   + sc.socket().getRemoteSocketAddress());
                buffer.rewind();
                sc.write(buffer);
                sc.close();
            }
        }
    }
}

//PipeDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

//管道是两个线程之间的单项数据连接,数据会被写到sink通道,从source通道读取
public class PipeDemo {
    public static void main(String[] args) throws IOException {
 		//获取通道
        Pipe pipe = Pipe.open();
        Pipe.SinkChannel sinkChannel = pipe.sink();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("hello".getBytes());
        buffer.flip();
        sinkChannel.write(buffer);
        Pipe.SourceChannel sourceChannel = pipe.source();
        buffer.flip();
        int len = sourceChannel.read(buffer);
        System.out.println(new String(buffer.array(), 0,len));
        sourceChannel.close();
        sinkChannel.close();
    }
}

//DatagramChannelDemo.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

//DatagramChannel是无连接的
public class DatagramChannelDemo {

    public static void main(String[] args) throws IOException {
        System.out.println("程序启动...");
    }

    private void sendDatagram() throws IOException {
        DatagramChannel sendChannel = DatagramChannel.open();
        InetSocketAddress sendAddress = new InetSocketAddress("localhost", 9999);
        while(true){
            ByteBuffer buffer = ByteBuffer.wrap("Hello World".getBytes());
            sendChannel.send(buffer, sendAddress);
            System.out.println("已经发送成功");
        }
    }

    private void receiveDatagram() throws IOException {
        DatagramChannel receiveChannel = DatagramChannel.open();
        InetSocketAddress receiveAddress = new InetSocketAddress(9999);
        receiveChannel.bind(receiveAddress);
        ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
        while(true){
            receiveBuffer.clear();
            SocketAddress socketAddress = receiveChannel.receive(receiveBuffer);
            receiveBuffer.flip();
            System.out.println(socketAddress.toString());
            System.out.println(receiveBuffer);
        }
    }
}

//FileChannelDemo1.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 通过FileChannel将数据写入缓冲区
 */
public class FileChannelDemo1 {
    public static void main(String[] args) throws IOException {
        //创建FileChannel
        RandomAccessFile file = 
            new RandomAccessFile("C:\\Mermaid使用.md", "rw");
        FileChannel channel = file.getChannel();
        //创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        //读取数据到buffer中
        int bytesRead = channel.read(buffer);
        while(bytesRead != -1){
            System.out.println("读取了:" + bytesRead);
            //读写模式转换
            buffer.flip();
            while(buffer.hasRemaining()){
                System.out.println((char)buffer.get());
            }
            //清除缓冲区内容
            buffer.clear();
            bytesRead = channel.read(buffer);
        }
        file.close();
        System.out.println("程序结束");
    }
}

//FileChannelDemo2.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 通过FileChannel从缓冲区读取数据
 */
public class FileChannelDemo2 {

    public static void main(String[] args) throws IOException {
        RandomAccessFile file = new RandomAccessFile("C:\\LICCD.txt", "rw");
        FileChannel channel = file.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String data = "Test 12345";
        buffer.clear();
        buffer.put(data.getBytes());
        buffer.flip();
        //需要循环写入
        while(buffer.hasRemaining()){
            channel.write(buffer);
        }
        channel.close();
    }

}

//FileChannelDemo3.java
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;

/**
 * 通道之间数据传输
 */
public class FileChannelDemo3 {
    public static void main(String[] args) throws IOException {
        RandomAccessFile aFile = new RandomAccessFile("C:\\01.txt", "rw");
        RandomAccessFile bFile = new RandomAccessFile("C:\\02.txt", "rw");
        FileChannel fromChannel = aFile.getChannel();
        FileChannel toChannel = bFile.getChannel();
        long position = 0;
        long size = fromChannel.size();
        //position() - 返回此通道文件位置, position(long) - 设置此通道的文件长度
        //transferFrom - 把数据从源通道传输到FileChannel中;
        //transferTo - 将数据从FileChannel传输到其他的FileChannel中
        toChannel.transferFrom(fromChannel, position, size);
        //fromChannel.transferTo(position, size, toChannel);
        aFile.close();
        bFile.close();
    }
}

//FileLockDemo.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
//文件锁的使用
public class FileLockDemo {
    public static void main(String[] args) throws IOException {
        String input = "Hello World!";
        ByteBuffer buffer = ByteBuffer.wrap(input.getBytes());
        String filePath = "01.txt";
        Path path = Paths.get(filePath);
        FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE,
                                               StandardOpenOption.APPEND);
        channel.position(channel.size() - 1);
        FileLock lock = channel.lock();
        System.out.println("是否共享锁:" + lock.isShared());
        channel.write(buffer);
        channel.close();

        FileReader fileReader = new FileReader(filePath);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String line = bufferedReader.readLine();
        System.out.println("读取出内容:");
        while(line != null){
            System.out.println(line);
            line = bufferedReader.readLine();
        }
        fileReader.close();
        bufferedReader.close();
    }
}

//AsyncFileChannelDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.CompletionHandler;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

/**
 * 文件通道异步读取
 */
public class AsyncFileChannelDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("01.txt");
        AsynchronousFileChannel fileChannel = 
            AsynchronousFileChannel.open(path, StandardOpenOption.READ);
        ByteBuffer buffer  = ByteBuffer.allocate(1024);
//        使用Future处理结果集
//        Future future = fileChannel.read(buffer, 0);
//        //判断是否完成
//        while(!future.isDone());
//        buffer.flip();
//        while(buffer.hasRemaining()){
//            System.out.print((char)buffer.get());
//        }
//        buffer.clear();
        //使用CompletionHandler接口处理结果
        fileChannel.read(buffer, 0, buffer, 
                         new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                attachment.flip();
                byte[] data = new byte[attachment.limit()];
                attachment.get(data);
                System.out.print(new String(data));
                attachment.clear();
            }

            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                System.out.println(exc.getMessage());
            }
        });
    }
}

Selector

Selector选择器也叫做多路复用器,用于检查一个或多个通道的状态是否可读写。使用Selector的好处在于使用更少的线程处理通道,避免多线程上下文切换带来的开销。不是所有的Channel都可以被Selector复用的,继承可选择通道SelectableChannel就可以复用。一个通道可以被注册到多个选择器上,而每个选择器只能被注册一次。

//SelectorDemo.java
public class SelectorDemo {
    public static void main(String[] args) throws IOException {
        //与Selector多路复用器一起使用时,Channel必须是非阻塞的
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        ssc.bind(new InetSocketAddress(8000));
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        Set<SelectionKey> selectionKeys = selector.selectedKeys();
        Iterator<SelectionKey> iterator = selectionKeys.iterator();
        while(iterator.hasNext()) {
            SelectionKey key = iterator.next();
            if (key.isAcceptable()) {
                System.out.println("accept");
            } else if (key.isConnectable()) {
                System.out.println("connect");
            } else if (key.isReadable()) {
                System.out.println("read");
            } else if (key.isWritable()) {
                System.out.println("write");
            }
            iterator.remove();
        }
    }
}

//SelectorTest.java
public class SelectorTest {
    @Test
    public void send() throws IOException {
        //获取通道、绑定主机和端口,切换到非阻塞模式,
        //创建Buffer写入数据,模式切换,写入通道,关闭
        SocketChannel sc = SocketChannel.open(new InetSocketAddress(8888));
        sc.configureBlocking(false);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put(new Date().toString().getBytes());
        buffer.flip();
        sc.write(buffer);
        buffer.close();
    }
    
    @Test
    public void recevie() throws IOException {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(8888));
        ssc.configureBlocking(false);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        Selector selector = Selector.open();
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        while(selector.select() > 0) {
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey key = iterator.next();
                if(key.isAcceptable()) {
                    //注册关心的事件
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel channel = (SocketChannel) key.channel();
                    int len = 0;
                    while((len = channel.read(buffer)) > 0) {
                        buffer.flip();
                        System.out.println(new String(buffer.array(), 0, len));
                        buffer.clear();
                    }
                }
                iterator.remove();
            }
        }
    }
}

//CharsetDemo.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public class CharsetDemo {
    public static void main(String[] args) throws IOException {
        Charset charset = Charset.forName("UTF-8");
        CharsetEncoder encoder = charset.newEncoder();
        CharBuffer charBuffer = CharBuffer.allocate(1024);
        charBuffer.put("醉里挑灯看剑");
        charBuffer.flip();
        ByteBuffer buffer = encoder.encode(charBuffer);
        System.out.println("编码之后的结果:");
        for(int i=0;i<buffer.limit();i++){
            System.out.print(buffer.get());
        }
        System.out.println();
        buffer.flip();
        CharsetDecoder decoder = charset.newDecoder();
        System.out.println("解码之后的结果:");
        System.out.println(decoder.decode(buffer));
    }
}

//PathDemo.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("C:\\2022\\01.txt");
        System.out.println(path);
        path = Paths.get("C:\\2022", "01");
        System.out.println(path);
        System.out.println(path.normalize());
        Path directory = Files.createDirectory(path);
        System.out.println(directory);
        Files.copy(path, directory);
    }
}

你可能感兴趣的:(#,IO,nio,java,网络)