• java new I/O


    java在1.4中引入了nio包,而且旧的io包也用nio重新实现过,同样享受速度上的提升.因为nio的结构更贴近操作系统执行io的方式:通道和缓冲器.

    唯一直接与管道通信的缓冲器为java.nio.ByteBuffer

    旧io中的三个类FileInputStream,FileOutputStream,RandomAccessFile被修改了,拥有可以获取管道的方法getChannel()方法.

    Reader和Writer这样的字符模式类不能产生管道,但是java.nio.channels.Channels类中提供了使用方法可以产生reader和writer.

    常用方法:

    • getChannel():用以产生管道
    • write():将byteBuffer写入管道
    • close():关闭管道
    • size():管道内容长度
    • wrap():将已存在数组包装入byteBuffer
    • allocate():分配ByteBuffer
    • read():将管道内容读入ByteBuffer
    • put():给ByteBuffer填充内容
    • clear():让byteBuffer做好填充数据的准备
    • flip():让byteBuffer做好被别人读取的准备
    • transgerTo()和transferFrom()将两个管道直接相连

    使用实例

    public static void main(String[] args) throws IOException {
        FileChannel fc = new FileOutputStream("out\out.dat").getChannel();
        fc.write(ByteBuffer.wrap("some text".getBytes()));
        fc.close();
        fc = new FileInputStream("out\out.dat").getChannel();
        ByteBuffer bf = ByteBuffer.allocate(1024);
        fc.read(bf);
        bf.flip();
        System.out.println("19 :" + bf.asCharBuffer());
        bf.rewind();
        String encoding = System.getProperty("file.encoding");
        System.out.println("22 :" + "decoded using " + encoding + ":
    " + Charset.forName(encoding).decode(bf));
        fc = new FileOutputStream("out\out.dat").getChannel();
        fc.write(ByteBuffer.wrap("some text".getBytes("UTF-16BE")));
        fc.close();
        fc = new FileInputStream("out\out.dat").getChannel();
        bf.clear();
        fc.read(bf);
        bf.flip();
        System.out.println("30 :" + bf.asCharBuffer().toString());
        fc = new FileOutputStream("out\out.dat").getChannel();
        bf = ByteBuffer.allocate(24);
        bf.asCharBuffer().put("some text");
        fc.write(bf);
        fc.close();
        fc = new FileInputStream("out\out.dat").getChannel();
        bf.clear();
        fc.read(bf);
        bf.flip();
        System.out.println("40 :" + bf.asCharBuffer());
    }

    可以对ByteBuffer使用视图缓冲器来写入和读取各种类型的数值.
    视图缓冲器asXXXBuffer (xxx对应各种基本类型),可以对视图缓冲期调用get()和put()方法.

    字节存放次序

    不同机器会有不同的字节排序方法,分为大端序和小端序,大端序为高位低字节,小端序为高位高字节.

    对于ByteBuffer可以使用order()方法改变byteBuffer的字节序,两者的区别为假如把一个16个字节的byteBuffer当作shortBuffer来get的话,
    get[0]的结果将会不同,对于大端序获得了高八位,小端序则获得低八位.

    小用法

    对于由数组支持的ByteBuffer可以使用array()方法显示视图底层的字节.对于非数组支持的byteBuffer使用会抛出UnsupportedOperationException

    用缓冲器操作数据

    byteBuffer是唯一将数据移进移出通道的方式,可以使用as方法从byteBuffer获得基本类型缓冲期,但是并不能把基本类型缓冲器转换为ByteBuffer.

    缓冲器的细节

    方法 描述
    capacity() 返回缓冲区容量
    clear() 清空缓冲区,将position设置为0,limit设置为容量.我们可以调用此方法复写缓冲区
    flip() 将limit设置为position,position设置为0.此方法用于准备从缓冲区读取已经写入的数据
    limit() 返回limit
    limit(int lim) 设置limit
    mark() 将mark设置为postion
    position() 返回position
    positon(int pos) 设置position
    remaining() 返回(limit-position)
    hasRemaining() 若有介于position和limit之间的元素则返回true

    内存映射文件

    内存映射文件允许我们创建和修改太大而不能载入内存的文件,有了内存映射文件,我们可以假定文件都在内存中,而且当作一个超大数组进行访问.

    static int length = 100;
    
    public static void main(String[] args) throws IOException {
        MappedByteBuffer out = new RandomAccessFile("out\test.out", "rw").getChannel()
                .map(FileChannel.MapMode.READ_WRITE, 0, length);
    
        for (int i = 0; i < length; i++) {
            out.put((byte) 'x');
        }
        System.out.println("finished writing");
        for (int i = length / 2; i < length / 2 + 6; i++) {
            System.out.print((char) out.get(i));
        }
    }

    文件加锁

    在jdk1.4引入了文件加锁机制,允许我们同步访问某个作为共享资源的文件,不过文件锁对于非java线程或是另一个虚拟机上的java线程是可见的.

    public static void main(String[] args) throws IOException, InterruptedException {
        FileOutputStream fos = new FileOutputStream("out\test.out");
        FileLock fl = fos.getChannel().tryLock();
        if (fl != null) {
            System.out.println("locked file");
            TimeUnit.MILLISECONDS.sleep(100);
            fl.release();
            System.out.println("released lock");
        }
        fos.close();
    }

    通过对fileChannel调用tryLock()或者lock(),就可以获得整个文件的fileLock.SocketChannel,DatagramChannel和ServerSocketChannel不需要加锁,因为他们是单进程实体继承而来,不能在多个进程之间共享.

    • tryLock() 非阻塞 尝试获得锁,如果不能获得将直接从调用处返回.
    • Lock() 阻塞 会阻塞进程知道获得锁 或者调用的线程中断,或者调用的通道关闭
    • FileLock.release() 释放锁.

    tryLock()和Lock() 还可以通过指定参数,对文件的一部分上锁 : (try)Lock(long position , long size , boolean shared )

    第三个参数表示是否共享锁.

    无参数的加锁方法会根据文件尺寸改变而改变,有参数的加锁方式不会改变,无论文件变大变小,超出position到positon+size的文件部分将不会被锁定.

    如果操作系统不支持.那么只能使用独占锁,锁的类型通过FileLock.isShared()查询

    对映射文件部分加锁

    由于内存映射文件通常用于超大文件,因此我们就可能需要使用部分加锁,以便其他线程更改文件的其他部分.例如数据库就是这样.因此多个用户可以同时访问到它.

    public static void main(String[] args) throws IOException {
        fc = new RandomAccessFile("out\test.out","rw").getChannel();
        MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE,0,length);
        for(int i = 0;i<length;i++){
            out.put((byte)'x');
        }
        new LockAndModify(out,0,length/2);
        new LockAndModify(out,length/2,length/2+length/4);
    
    }
    
    private static class LockAndModify extends Thread{
        private ByteBuffer buff;
        private int start,end;
        LockAndModify(ByteBuffer mbb,int start, int end){
            this.start = start;
            this.end = end;
            mbb.limit(end);
            mbb.position(start);
            buff = mbb.slice();
            start();
        }
        public void run(){
            try{
                FileLock fl = fc.lock(start,end,false);
                System.out.println("locked :"+start+" to "+end);
                while (buff.position()<buff.limit()-1){
                    buff.put((byte)(buff.get()+1));
                }
                fl.release();
                System.out.println("release :"+start +" to "+end);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    static int length = 100;
    static FileChannel fc ;

    当尝试一个两个线程的文件锁区域有所重合时,抛出了一个错误,暂时还没弄懂





  • 相关阅读:
    linux安装vsftpd服务器
    安装Twisted
    py文件转换为exe文件
    Python实现批量新建SecureCRT Session
    常见的字符编码
    心得 : 面向对象和面向过程的区别
    Apache配置HTTPS的过程小记
    关于oracle的sequence和trigger。
    oracle在drop表时要注意
    mysql中整数类型后面的数字,是不是指定这个字段的长度?比如int(11),11代表11个字节吗?
  • 原文地址:https://www.cnblogs.com/renluxiang/p/b6b697530ed2858510f32aa780d54d53.html
Copyright © 2020-2023  润新知