IO学霸2 -- NIO

11. NIO

JDK1.4的java.nio.*已经加入新的IO类库,目的在于提高速度。实际上旧的IO包已经使用nio重写,即使不显示用nio写代码,速度已经得到提升。

这种速度提升得益于使用的结构接近于操作系统执行IO的方式:通道与缓冲器。

唯一直接与通道交互的缓冲器:ByteBuffer。

产生FileChannel的三个类:FileInputStream, FileOutputStream, RandomAccessFile.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class GetChannel {

	private static final int BSIZE = 1024;
	
	public static void main(String[] args) throws IOException {
		FileChannel fc = new FileOutputStream("data.txt").getChannel();
		fc.write(ByteBuffer.wrap("Some text ".getBytes()));
		fc.close();
		fc = new RandomAccessFile("data.txt","rw").getChannel();
		fc.position(fc.size());
		fc.write(ByteBuffer.wrap("Some more".getBytes()));
		fc.close();
		fc = new FileInputStream("data.txt").getChannel();
		ByteBuffer buff = ByteBuffer.allocate(BSIZE);
		fc.read(buff);
		buff.flip();
		while(buff.hasRemaining()){
			System.out.print((char)buff.get());
		}
	}

}
//*Output:
Some text Some more
*//

文件复制,注意flip()与clear()的使用

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelCopy {
	private static final int BSIZE = 1024;
	public static void main(String[] args) throws IOException {
		if(args.length!=2){
			System.out.println("args shit");
			System.exit(1);
		}
		FileChannel in = new FileInputStream("D:\\javaLD\\workspace_IBM\\IO1\\src\\com\\ld\\tij\\ChannelCopy.java").getChannel(),
		out = new FileOutputStream(args[1]).getChannel();
		ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
		while(in.read(buffer)!=-1){
			buffer.flip();
			out.write(buffer);
			buffer.clear();
		}
		//method 2:改进方法transferTo()/transferFrom() 可以将一个通道与另外一个通道相连
		in.transferTo(0, in.size(), new FileOutputStream("test2.txt").getChannel());
	}

}

11.1 NIO中转换数据

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;

public class BufferToText {
	private static final int BSIZE = 1024;
	public static void main(String[] args) throws IOException {

		FileChannel fc = new FileOutputStream("data2.txt").getChannel();
		fc.write(ByteBuffer.wrap("Some text".getBytes()));
		fc.close();
		
		fc = new FileInputStream("data2.txt").getChannel();
		ByteBuffer buff = ByteBuffer.allocate(BSIZE);
		fc.read(buff);
		buff.flip();
		System.out.println(buff.asCharBuffer());
		
		buff.rewind();
		String encoding = System.getProperty("file.encoding");
		System.out.println("Decoded using "+encoding+": "+ Charset.forName(encoding).decode(buff));
		
		fc = new FileOutputStream("data2.txt").getChannel();
		fc.write(ByteBuffer.wrap("Some text by utf".getBytes("UTF-16BE")));
		fc.close();
		fc = new FileInputStream("data2.txt").getChannel();
		buff.clear();
		fc.read(buff);
		buff.flip();
		System.out.println(buff.asCharBuffer());
		
		fc = new FileOutputStream("data2.txt").getChannel();
		buff = ByteBuffer.allocate(100);
		buff.asCharBuffer().put("some text as charbuffer");
		fc.write(buff);
		fc.close();
		fc = new FileInputStream("data2.txt").getChannel();
		buff.clear();
		fc.read(buff);
		buff.flip();
		System.out.println(buff.asCharBuffer());
	}

}
//*output:
????
Decoded using GBK: Some text
Some text by utf
some text as charbuffer
*//

打印第一行错误原因是因为既没有编码也没有解码。

第二行有解码

第三行有编码

第四行读写都指定了相应的数据格式

在这个例子基础上,下面是一个对各种数据类型的例子

import java.nio.ByteBuffer;

public class GetData {

	private static final int BSIZE = 1024;
	
	public static void main(String[] args) {
		ByteBuffer bb = ByteBuffer.allocate(BSIZE);
		
		int i =0;
		while(i++<bb.limit()){
			if(bb.get()!=0)
				System.out.println("nonzero");
		}
		System.out.println("i="+i);
		bb.rewind();
		
		bb.asCharBuffer().put("Howdy!");
		char c;
		while((c=bb.getChar())!=0){
			System.out.print(c+" ");
		}
		System.out.println();
		bb.rewind();
		
		bb.asShortBuffer().put((short)471142);
		System.out.println(bb.getShort());
		bb.rewind();
		
		bb.asIntBuffer().put(99471142);
		System.out.println(bb.getInt());
		bb.rewind();
		
		bb.asLongBuffer().put(99471142);
		System.out.println(bb.getLong());
		bb.rewind();
		
		bb.asFloatBuffer().put(99471142);
		System.out.println(bb.getFloat());
		bb.rewind();
		
		bb.asDoubleBuffer().put(99471142);
		System.out.println(bb.getDouble());
		bb.rewind();
		
	}

}
//*Output:
i=1025
H o w d y ! 
12390
99471142
99471142
9.9471144E7
9.9471142E7
*//

11.2 视图缓冲器

通过某个特定的基本数据类型的view查看其底层的ByteBuffer。ByteBuffer依然是实际保存数据的地方,并支持前面的view,所以对view任何修改都会映射成为修改后ByteBuffer中的数据。

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

public class IntBufferDemo {
	private static final int BSIZE = 1024;
	public static void main(String[] args) {
		ByteBuffer bb = ByteBuffer.allocate(BSIZE);
		IntBuffer ib = bb.asIntBuffer();
		ib.put(new int[]{11,42,47,99,143,811,1016});
		System.out.println("ib3:"+ib.get(3));
		ib.put(3, 1811);
		ib.flip();
		while(ib.hasRemaining()){
			System.out.println(ib.get());
		}
	}

}
//*Output:
ib3:99
11
42
47
1811
143
811
1016
*//

在同一个ByteBuffer建立不同的view buffer,将同一个字节序列翻译成short, int, float, long, double.

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;

public class ViewBuffers {

	public static void main(String[] args) {
		ByteBuffer bb = ByteBuffer.wrap(new byte[]{0,0,0,0,0,0,0,'a'});
		bb.rewind();
		System.out.println("Byte Buffer ");
		while(bb.hasRemaining()){
			System.out.print(bb.position()+"->"+bb.get()+", ");
		}
		System.out.println();
		
		CharBuffer cb = ((ByteBuffer)bb.rewind()).asCharBuffer();
		System.out.println("Char Buffer");
		while(cb.hasRemaining()){
			System.out.print(cb.position()+"->"+cb.get()+", ");
		}
		System.out.println();
		
		FloatBuffer fb = ((ByteBuffer)bb.rewind()).asFloatBuffer();
		System.out.println("Float Buffer");
		while(fb.hasRemaining()){
			System.out.print(fb.position()+"->"+fb.get()+", ");
		}
		System.out.println();
		
		IntBuffer ib = ((ByteBuffer)bb.rewind()).asIntBuffer();
		System.out.println("Int Buffer");
		while(ib.hasRemaining()){
			System.out.print(ib.position()+"->"+ib.get()+", ");
		}
		System.out.println();
		
		LongBuffer lb = ((ByteBuffer)bb.rewind()).asLongBuffer();
		System.out.println("Long Buffer");
		while(lb.hasRemaining()){
			System.out.print(lb.position()+"->"+lb.get()+", ");
		}
		System.out.println();
		
		ShortBuffer sb = ((ByteBuffer)bb.rewind()).asShortBuffer();
		System.out.println("Short Buffer");
		while(sb.hasRemaining()){
			System.out.print(sb.position()+"->"+sb.get()+", ");
		}
		System.out.println();
		
		DoubleBuffer db = ((ByteBuffer)bb.rewind()).asDoubleBuffer();
		System.out.println("Double Buffer");
		while(db.hasRemaining()){
			System.out.print(db.position()+"->"+db.get()+", ");
		}
		System.out.println();
		
	}

}
//*output:
Byte Buffer 
0->0, 1->0, 2->0, 3->0, 4->0, 5->0, 6->0, 7->97, 
Char Buffer
0-> , 1->, 2-> , 3-> a,
Float Buffer
0->0.0, 1->1.36E-43, 
Int Buffer
0->0, 1->97, 
Long Buffer
0->97, 
Short Buffer
0->0, 1->0, 2->0, 3->97, 
Double Buffer
0->4.8E-322, 
*//
ByteBuffer是8字节数组产生,然后通过不同的基本类型view buffer显示出来,如下图:



11.2.1字节存放次序(二进制)

big endian 高位优先:将最重要的字节存放在地址最低的存储单元。

little endian 低位优先:讲最重要的字节存放在地址最高的存储单元。

ByteBuffer以高位优先存储数据,网上传送也常用此方式。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;

public class Endians {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		String s = "abcdef";
		ByteBuffer bb = ByteBuffer.wrap(new byte[12]);
		bb.asCharBuffer().put(s);
		System.out.println(Arrays.toString(bb.array()));
		bb.rewind();
		bb.order(ByteOrder.BIG_ENDIAN);
		bb.asCharBuffer().put(s);
		System.out.println(Arrays.toString(bb.array()));
		bb.rewind();
		bb.order(ByteOrder.LITTLE_ENDIAN);
		bb.asCharBuffer().put(s);
		System.out.println(Arrays.toString(bb.array()));
	}

}
//*Output:
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
[0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102]
[97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0]
*//



11.2.2 深入缓冲区

Buffer由数据和四个索引组成:mark, position, limit, capacity。

capacity(): 返回缓冲区容量;

clear(): 清空缓冲区,position=0, limit=capacity();

flip(): limit=position, position=0;

limit(): 返回limit值;

limit(int lim): 设置limit值;

mark(): mark=position;

position(): 返回position值;

position(int pos): 设置position值;

remaining(): 返回(limit - position);

hasRemaining(): 如果存在position和limit之间元素(limit-position>0?),则返回ture;

reset(): position=mark.



例子:交换缓冲器中相邻的字符

import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class UsingBuffers {

	private static void symmetricScramble(CharBuffer buffer){
		while(buffer.hasRemaining()){
			buffer.mark();
			char c1 = buffer.get();
			char c2 = buffer.get();
			buffer.reset();
			buffer.put(c2).put(c1);
		}
	}
	
	
	public static void main(String[] args) {
		char[] data = "UsingBuffers".toCharArray();
		ByteBuffer bb = ByteBuffer.allocate(data.length*2);
		CharBuffer cb = bb.asCharBuffer();
		cb.put(data);
		System.out.println(cb.rewind());
		symmetricScramble(cb);
		System.out.println(cb.rewind());
		symmetricScramble(cb);
		System.out.println(cb.rewind());
	}

}
//*Output:
UsingBuffers
sUniBgfuefsr
UsingBuffers
*//

11.2.3 内存映射文件

内存映射文件用于创建和修改因为太大不能放入内存的文件,使用这种方式,我们可以假定这个文件都放在内存中,而且可以完全把它当做巨大的数组来访问。

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class LargeMappedFiles {

	static int length = 0x8FFFFFF; //128MB
	
	public static void main(String[] args) throws IOException, Exception {
		// TODO Auto-generated method stub
		MappedByteBuffer out 
			= new RandomAccessFile("test.dat","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+60;i++){
			System.out.print((char)out.get(i));
		}
	}

}
//*Output:
Finished writing
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*//

11.2.4 加锁

import java.io.FileOutputStream;
import java.nio.channels.FileLock;
import java.util.concurrent.TimeUnit;

public class FileLocking {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		FileOutputStream fos = new FileOutputStream("file.txt");
		FileLock fl = fos.getChannel().tryLock();
		if(fl!=null){
			System.out.println("Locked File");
			TimeUnit.MILLISECONDS.sleep(1000);
			fl.release();
			System.out.println("Released Lock");
		}
		fos.close();
	}

}
//*Output
Locked File
Released Lock
*//
这里是对整个文件加锁,tryLock()是非阻塞式的,它设法获得锁,否则直接在方法中返回。lock()是阻塞式,阻塞进程直至可以获得锁,或者调用lock()线程中断,或者lock()的通道关闭,FileLock.release()可以释放锁。

11.2.5 部分加锁

tryLock(long position, long size, boolean shared) / lock(long position, long size, boolean shared)

无参数加锁根据文件大小变化而变化,固定大小加锁不会随着文件变化变化,如果获得一段区域加锁(position~position+size),当文件增大时候,position+size以外的部分将不会被锁定。

对于独占锁或者共享锁由底层的操作系统所决定。如果操作系统不支持共享锁并为每一个请求都创建一个锁,那么它就是独占锁,FileLock.isShared()可以查询。

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class LockingMappedFiles {

	static final int LENGTH = 0x8FFFFFF;//128MB
	static FileChannel fc;
	
	public static void main(String[] args) throws IOException {
		fc = new RandomAccessFile("test.dat", "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,0+LENGTH/3);
		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();
		}
		@Override
		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("Released: "+start+" to "+end);
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		
	}

}
//*Output
Locked: 0 to 50331647
Locked: 75497471 to 113246206
Released: 75497471 to 113246206
Released: 0 to 50331647
*//

finished nio,IO未完持续~:)






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值