zoukankan      html  css  js  c++  java
  • java的nio

    1.nio的简介

    nio的FileChannel类可以获取的方法有 FileInputStream和FileOutputStream以及RandomAccessFile的getChannel方法,或者FileChannel类的open方法等获取,SocketChannel,ServerSocketChannel,DatagramChannel,同样可以Socket,ServerSocket的getChannel方法和open方法(tcp)等获取,DatagramChannel可以通过open方法获取(udf)等方法获取,Pipe也可以同过open等方法获取,他们只是建立通道,并不参与数据的输入和输出,输入和输出是buffer完成的,buffer有直接缓存区和非直接缓存区两种,直接缓存建立在物理内存上的,建立和销毁allocateDirect的空间的开销比较大,传递的效率要比非直接缓冲区要好(因为直接缓存区只需要复制一次,而非直接缓存区需要复制两次,直接缓存区较非直接缓存区减少了一次复制),所以通常用于传递allocateDirect的空间开创的比较大的时候,用利于传输比较大的文件,但是太大例如超过几个G,那么可以通过分段的方式来传输,非直接缓存区是建立在堆上的,受到jvm的限制。

     2.同步与异步

    同步是发送一个请求后需要等待返回后才可以发送下一条指令,异步没有等待的过程可以发下一条指令,就例如我叫你吃饭,你没来,我就一直等,这就是同步,你没来我就自己吃饭叫做异步

    3.阻塞与非阻塞

    阻塞相当于一个队里,我与他建立连接以后,那么需要我先完成,后面的都都排队先等着,非阻塞是你们这些需要与我建立连接的部分,我用一个或者多个线程来轮询你们,你们谁先准备就绪就运行谁。

    4.Buffer实现的原理

    常用的方法有position limit capacity  mark  reset  rewind  hasRemaining remaining  flip  clear isDirect等以及get,put方法(position会相应的移动)但是又较多的重载方法,没有参数的表示返回游标位置的数据或者放入数据,数组表示向数组中放入数据或者向数组中写入数据。

    public class ByteBufferTest {
    	public static void main(String[] args) {
    		ByteBuffer buffer = ByteBuffer.allocate(10);
    		if (!buffer.isDirect()) {
    			System.out.println("非直接缓冲区");// 非直接缓冲区
    		}
    		System.out.println(buffer.position());// 位置0
    		System.out.println(buffer.limit());// 界限10
    		System.out.println(buffer.capacity());// 开辟的空间10
    		buffer.put((byte) 'a');
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置1
    		System.out.println(buffer.limit());// 界限10
    		System.out.println(buffer.capacity());// 开辟的空间10
    		byte[] bys = { (byte) 'b', (byte) 'c' };
    		buffer.put(bys);
    		buffer.mark();
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置3
    		System.out.println(buffer.limit());// 界限10
    		System.out.println(buffer.capacity());// 开辟的空间10
    		buffer.reset();
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置1
    		System.out.println(buffer.limit());// 界限10
    		System.out.println(buffer.capacity());// 开辟的空间10
    		buffer.flip();// 将limit设置为当前位置,position设置为0
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置0
    		System.out.println(buffer.limit());// 界限1
    		System.out.println(buffer.capacity());// 开辟的空间10
    		buffer.rewind();// limit不变,position变为0,且去除mark
    		buffer.flip();
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置0
    		System.out.println(buffer.limit());// 界限0
    		System.out.println(buffer.capacity());// 开辟的空间10
    		try {
    			buffer.reset();
    			System.out.println("-------------");
    			System.out.println(buffer.position());
    			System.out.println(buffer.limit());
    			System.out.println(buffer.capacity());
    		} catch (Exception e) {
    			System.out.println("mark标记不存在了");
    		}
    		buffer.limit(3);
    		buffer.position(3);//position<=limit<=capacity
    		buffer.flip();
    		System.out.println(buffer.remaining());//position与limit之间还剩多少元素3
    		System.out.println("-------------");
    		System.out.println(buffer.position());// 位置0
    		System.out.println(buffer.limit());// 界限3
    		System.out.println(buffer.capacity());// 开辟的空间10
    		while(buffer.hasRemaining()){//position和limit之间是否还有元素
    			System.out.println((char)buffer.get());// a b c
    		}
    		System.out.println("------------");
    		System.out.println(buffer.position());// 位置3
    		System.out.println(buffer.limit());// 界限3
    		System.out.println(buffer.capacity());// 开辟的空间10
    		byte[] bys1 = { (byte) 'd', (byte) 'e', (byte) 'f'};
    		buffer.limit(9);
    		buffer.put(bys1);
    		buffer.position(7);
    		/*limit变为等于capacity,position的位置等于limit-position,在limit-position的
    		数据相应的往前移覆盖position开始的同样多的数据*/
    		buffer.compact();
    		System.out.println("------------");
    		System.out.println(buffer.position());// 位置2
    		System.out.println(buffer.limit());// 界限10
    		System.out.println(buffer.capacity());// 开辟的空间10
    		while(buffer.hasRemaining()){//position和limit之间是否还有元素
    			System.out.println((char)buffer.get());// c d e f 4个空格
    		}
    	}
    }
    

     其他的buffer类似

    5.FileChannel的获取方式与使用方法

    public class FileChannelTest {
    	public static void main(String[] args) throws IOException {
    
    		// 非直接缓冲区
    		// 方式1
    		FileChannel channel1 = null;
    		FileChannel channel2 = null;
    		try {
    			channel1 = new FileInputStream("1.txt").getChannel();
    			channel2 = new FileOutputStream("3.txt").getChannel();
    			ByteBuffer allocate = ByteBuffer.allocate(1024);
    			while (channel1.read(allocate) != -1) {
    				allocate.flip();
    				channel2.write(allocate);
    				allocate.clear();
    			}
    		} finally {
    			channel1.close();
    			channel2.close();
    		}
    		// 方式2
    		FileChannel channel3 = new RandomAccessFile("1.txt", "r").getChannel();
    		FileChannel channel4 = new RandomAccessFile("4.txt", "rw").getChannel();
    		ByteBuffer allocate1 = ByteBuffer.allocate(1024);
    		while (channel3.read(allocate1) != -1) {
    			allocate1.flip();
    			channel4.write(allocate1);
    			allocate1.clear();
    		}
    		channel3.close();
    		channel4.close();
    		// 方式3
    		FileChannel open1 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
    		// StandardOpenOption.CREATE
    		// 有就覆盖,没有就创建,StandardOpenOption.CREATE_NEW有就报错,没有就创建
    		FileChannel open2 = FileChannel.open(Paths.get("5.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
    				StandardOpenOption.CREATE_NEW);
    		ByteBuffer allocate2 = ByteBuffer.allocate(1024);
    		while (open1.read(allocate2) != -1) {
    			allocate2.flip();
    			open2.write(allocate2);
    			allocate2.clear();
    		}
    		open1.close();
    		open2.close();
    		// 直接缓冲区
    		// 方式1
    		FileChannel channel5 = null;
    		FileChannel channel6 = null;
    		try {
    			channel5 = new FileInputStream("1.txt").getChannel();
    			channel6 = new FileOutputStream("6.txt").getChannel();
    			MappedByteBuffer map = channel5.map(MapMode.READ_ONLY, 0, channel5.size());
    			channel6.write(map);
    		} finally {
    			channel5.close();
    			channel6.close();
    		}
    		// 方式2
    		FileChannel open3 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
    		// StandardOpenOption.CREATE有就覆盖,没有就创建,StandardOpenOption.CREATE_NEW有就报错,没有就创建
    		FileChannel open4 = FileChannel.open(Paths.get("7.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
    				StandardOpenOption.CREATE_NEW);
    		ByteBuffer allocate3 = ByteBuffer.allocateDirect(1024);
    		while (open3.read(allocate3) != -1) {
    			allocate3.flip();
    			open4.write(allocate3);
    			allocate3.clear();
    		}
    		open3.close();
    		open4.close();
    		// 方式3
    		FileChannel open5 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
    		// StandardOpenOption.CREATE
    		// 有就覆盖,没有就创建,StandardOpenOption.CREATE_NEW有就报错,没有就创建
    		FileChannel open6 = FileChannel.open(Paths.get("8.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
    				StandardOpenOption.CREATE_NEW);
    		MappedByteBuffer map1 = open5.map(MapMode.READ_ONLY, 0, open4.size());
    		MappedByteBuffer map2 = open6.map(MapMode.READ_WRITE, 0, open4.size());
    		// open4.size()不能太大否则会超出int的范围
    		byte[] dst1 = new byte[(int) open4.size()];
    		// 将数据放入dst1中
    		map1.get(dst1);
    		// 将dst1写入文件中
    		map2.put(dst1);
    		open5.close();
    		open6.close();
    		// 方式4
    		FileChannel open7 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
    		// StandardOpenOption.CREATE
    		// 有就覆盖,没有就创建,StandardOpenOption.CREATE_NEW有就报错,没有就创建
    		FileChannel open8 = FileChannel.open(Paths.get("9.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
    				StandardOpenOption.CREATE_NEW);
    		open7.transferTo(0, open7.size(), open8);
    		// open8.transferTo(position, count, target)
    		open7.close();
    		open8.close();
    	}
    }
    

    6.直接缓存区域与非直接缓冲区的效率比较

    public static void main(String[] args) throws IOException {
           long  start=System.currentTimeMillis();
           String filePath="F:/test/1.wmv";
           FileChannel inch1 = new  FileInputStream(filePath).getChannel();
           FileChannel outch1 = new  FileOutputStream("2.wmv").getChannel();
           ByteBuffer bf1 = ByteBuffer.allocate(2);
           while(inch1.read(bf1)!=-1){
               bf1.flip();
               outch1.write(bf1);
               bf1.clear();
           }
           inch1.close();
           outch1.close();
           long end=System.currentTimeMillis();
           System.out.println(end-start);//189388
         //当allocate为4的时候,时间为100988
    System.gc(); System.runFinalization(); start=System.currentTimeMillis(); FileChannel inch2 = new FileInputStream(filePath).getChannel(); FileChannel outch2 = new FileOutputStream("3.wmv").getChannel(); ByteBuffer bf2 = ByteBuffer.allocateDirect(2); while(inch2.read(bf2)!=-1){ bf2.flip(); outch2.write(bf2); bf2.clear(); } inch2.close(); outch2.close(); end=System.currentTimeMillis(); System.out.println(end-start);//190209 } }
    public static void main(String[] args) throws IOException {
           long  start=System.currentTimeMillis();
           String filePath="F:/test/1.wmv";
           FileChannel inch1 = new  FileInputStream(filePath).getChannel();
           FileChannel outch1 = new  FileOutputStream("2.wmv").getChannel();
           ByteBuffer bf1 = ByteBuffer.allocate(10000);
           while(inch1.read(bf1)!=-1){
               bf1.flip();
               outch1.write(bf1);
               bf1.clear();
           }
           inch1.close();
           outch1.close();
           long end=System.currentTimeMillis();
           System.out.println(end-start);//200
           System.gc();
           System.runFinalization();
           start=System.currentTimeMillis();
           FileChannel inch2 = new  FileInputStream(filePath).getChannel();
           FileChannel outch2 = new  FileOutputStream("3.wmv").getChannel();
           ByteBuffer bf2 = ByteBuffer.allocateDirect(10000);
           while(inch2.read(bf2)!=-1){
               bf2.flip();
               outch2.write(bf2);
               bf2.clear();
           }
           inch2.close();
           outch2.close();
           end=System.currentTimeMillis();
           System.out.println(end-start);//90
           System.gc();
           System.runFinalization();
           start=System.currentTimeMillis();
           FileChannel inch3 = new  FileInputStream(filePath).getChannel();
           FileChannel outch3 = new  FileOutputStream("4.wmv").getChannel();
           inch3.transferTo(0, inch3.size(), outch3);
           inch3.close();
           outch3.close();
           end=System.currentTimeMillis();
           System.out.println(end-start);//33
    }
    }

    7.SocketChannel与ServerSocketChannel(TCP)

    阻塞式

    @Test
        public void  SocketChannelTest() throws IOException{
            SocketChannel open1 = SocketChannel.open();
            open1.connect(new  InetSocketAddress("127.0.0.1", 9898));
            FileChannel open2 = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            while(open2.read(allocate)!=-1){
                allocate.flip();
                open1.write(allocate);
                allocate.clear();
            }
            //关闭输入,解决阻塞问题
            open1.shutdownInput();
            open1.read(allocate);
            allocate.flip();
            String string = new  String(allocate.array());
            System.out.println(string);
            open1.close();
            open2.close();
        }
        @Test
        public void serverSocketChannelTest() throws IOException{
            ServerSocketChannel open1 = ServerSocketChannel.open();
            open1.bind(new InetSocketAddress(9898));
            //阻塞式的方法
            SocketChannel accept = open1.accept();
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            FileChannel open2 = FileChannel.open(Paths.get("10.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE);
            while(accept.read(allocate)!=-1){
                allocate.flip();
                open2.write(allocate);
                allocate.clear();
            }
            open2.close();
            allocate.put("传输完成".getBytes());
            accept.write(allocate);
            open1.close();
        }

    非阻塞式selector

    Channel和Selector配合使用,Channel必须是非阻塞的才能注册到selector上,SocketChannel、ServerSocketChannel、DatagramChannel、Pipe.SinkChannel、Pipe.SourceChannel能使用,FileChannel不适用Selector,因为FileChannel不能切换为非阻塞模式,FileChannel没有继承SelectableChannel;Selector的select方法是一个阻塞式的方法,需要返回值大于0,register()方法的第二个参数,这是一个”interest集合“,Selector对象会包含3种类型的SelectionKey集合,all-keys集合 (当前所有向Selector注册的SelectionKey的集合,Selector的keys()方法返回该集合),selected-keys集合(相关事件已经被Selector捕获的SelectionKey的集合,Selector的selectedKeys()方法返回该集合),cancelled-keys集合 (已经被取消的SelectionKey的集合,Selector没有提供访问这种集合的方法,取消了就不能再利用,所以一般不能用cancel方法),如果关闭了与SelectionKey对象关联的Channel对象,或者调用了SelectionKey对象的cancel方法,这个SelectionKey对象就会被加入到cancelled-keys集合中,当register()方法执行时,新建一个SelectioKey,并把它加入Selector的all-keys集合中SelectionKey就被加入到selected-keys集合中,程序直接调用selected-keys集合的remove()方法,或者调用它的iterator的remove()方法,可以从selected-keys集合中删除一个SelectionKey对象。

       @Test
        public void SocketChannelTest() throws IOException {
            SocketChannel open1 = SocketChannel.open();
            open1.connect(new InetSocketAddress("127.0.0.1", 9898));
            // 设置为非阻塞式
            open1.configureBlocking(false);
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            Scanner scanner = new Scanner(System.in);
            while (true) {
                // scanner.nextLine();阻塞式的
                String next = scanner.nextLine();
                if (next.equals("exit")) {
                    break;
                }
                allocate.put(next.getBytes());
                allocate.flip();
                open1.write(allocate);
                allocate.clear();
            }
            open1.close();
        }
    
        @Test
        public void serverSocketChannelTest() throws IOException {
            ServerSocketChannel open1 = ServerSocketChannel.open();
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            open1.bind(new InetSocketAddress(9898));
            // 设置为非阻塞式
            open1.configureBlocking(false);
            Selector selector = Selector.open();
            // 将该通道注册到选择器上,SelectionKey.OP_ACCEPT 接收就绪:准备好接收新进入的连接
            // 还有读就绪 ,写就绪,连接就绪
            open1.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // selector.select() 阻塞式的方法,至少有一个通道被选择时才返回。当这个方法被执行时,当前线程是允许被中断
                while (selector.select() > 0) {
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectedKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        if (key.isAcceptable()) {
                            SocketChannel channel = open1.accept();
                            channel.configureBlocking(false);
                            channel.register(selector, SelectionKey.OP_READ);
                        } else {
                            SocketChannel channel = (SocketChannel) key.channel();
                            allocate.clear();
                            channel.read(allocate);
                            allocate.flip();
                            System.out.println(new String(allocate.array()));
                        }
                        iterator.remove();
                    }
                }
            }
        }

    8.DatagramChannel

        @Test
        public  void  client() throws IOException{
            DatagramChannel open = DatagramChannel.open();
            open.configureBlocking(false);
            Scanner scanner = new  Scanner(System.in);
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            while(true){
                String nextLine = scanner.nextLine();
                if(nextLine.equals("exit")){
                    break;
                }
                allocate.put(new String("你好!").getBytes());
                allocate.flip();
                open.send(allocate, new  InetSocketAddress("127.0.0.1", 9898));
                allocate.clear();
            }
            open.close();
        }
        @Test
        public void  server() throws IOException{
            DatagramChannel open = DatagramChannel.open();
            open.configureBlocking(false);
            open.bind(new InetSocketAddress(9898));
            Selector select = Selector.open();
        //没有SelectionKey.OP_ACCEPT open.register(
    select, SelectionKey.OP_READ); ByteBuffer allocate = ByteBuffer.allocate(1024); while(true){ //阻塞式 while(select.select()>0){ Set<SelectionKey> selectedKeys = select.selectedKeys(); Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); if(key.isReadable()){ allocate.clear(); open.receive(allocate); allocate.flip(); System.out.println(new String (allocate.array())); } iterator.remove(); } } } } }

    9.nio的锁(见后续juc)

    lock:并且在获得锁之前,该线程将一直处于休眠状态。

    tryLock:仅在调用时锁为空闲状态才获取该锁。如果锁可用则立即获取锁,返回true。如果锁不可用,则此方法返回false。

    10.直接缓存区的map映射的分段读取(在物理内存不够用的情况的,或者严重影响效率的情况下)

    public class SegmentDirTest {
        public static void main(String[] args) throws IOException {
            FileChannel open = FileChannel.open(Paths.get("1.txt"), StandardOpenOption.READ);
            FileChannel open1 = FileChannel.open(Paths.get("11.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE,
                    StandardOpenOption.CREATE);
            // position开始位置, size大小
            MappedByteBuffer map = open.map(MapMode.READ_ONLY, 0, 2);
    //        System.out.println(map.position());//0
            open1.write(map);
            map.clear();
            map = open.map(MapMode.READ_ONLY, 2, open.size() - 2);
            open1.write(map);
            open.close();
            open1.close();
        }
    }
  • 相关阅读:
    JDBC事务处理
    JDBC之LOB数据类型
    使用JDBC驱动程序处理元数据
    JDBC之PreparedStatement
    JDBC主要API学习总结
    JDBC简介
    ForkJoinPool 分支/合并框架
    线程池
    线程八锁
    jQuery中的事件冒泡
  • 原文地址:https://www.cnblogs.com/gg128/p/9357311.html
Copyright © 2011-2022 走看看