最近在项目中,频繁使用到ByteBuffer,对于其中的一些用法感到迷惑,于是在闲暇的时间,查看了jdk文档,并使用少量的代码测试了对几个方法的使用,分享给大家。
1.传输数据
在jdk.doc里有说明:
此类的每个子类都定义了两种获取 和放置 操作:
相对 操作读取或写入一个或多个元素,它从当前位置开始,然后将位置增加所传输的元素数。如果请求的传输超出限制,则相对获取 操作将抛出
BufferUnderflowException
,相对放置 操作将抛出BufferOverflowException
;这两种情况下,都没有数据被传输。绝对 操作采用显式元素索引,该操作不影响位置。如果索引参数超出限制,绝对获取 操作和放置 操作将抛出
IndexOutOfBoundsException
。
当然,通过适当通道的 I/O 操作(通常与当前位置有关)也可以将数据传输到缓冲区或从缓冲区传出数据。
上面的说明初次看会让我很困惑,于是写下了一个测试的方法。
ByteBuffer b = ByteBuffer.allocate(1000);
b.put(new byte[]{0x68}); //将数据塞入缓冲区
b.flip(); //只有在flip之后才可以使用塞入的数据哦。
System.out.println(HexDump.hexDump(b));
输出的结果:68
上面都是没有问题的,接着看下面的。
在上面flip()之后,我再向里面塞入数据,是什么样的结果呢?
b.put(new byte[]{0x61}); //看清楚长度,跟上面一样
b.flip();//再次flip
System.out.println(HexDump.hexDump(b));
输出的结果:61
好的,这样都是没有问题的,无论你向里面插入什么数据(只要长度不超过第一次)都是可以的。
好的,我现在如果向里面塞入两个字节的数据,看一下什么情况。
b.put(new byte[]{0x61,0x62});
b.flip();
System.out.println(HexDump.hexDump(b));
Exception in thread "main" java.nio.BufferOverflowException
at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:165)
at java.nio.ByteBuffer.put(ByteBuffer.java:813)
at test._bytebuffer.TestCompact.main(TestCompact.java:16)
//oh shit!!!
看到错误之后,不要着急,看一下jdc.doc说到的。 这样就属于超过相对数量的传输。
那么这种情况下,我还想用b这个变量,而我就想放两个字节的数据应该怎么办呢?
非常好办啦,在put之前,调用一下b.clear()就可以了。
但是,这样还有一个问题,我不仅要放置两个字节的数据,在输出的时候,我还想将前面的数据也输出出来,这应该怎么办呢?
那我们就是用compact方法,记住在每次put、filp之后调用compact方法,否则就达不到效果了。
ByteBuffer b = ByteBuffer.allocate(1000); b.put(new byte[]{0x68}); b.flip(); b.compact(); System.out.println(HexDump.hexDump(b)); b.put(new byte[]{0x61}); b.flip(); b.compact(); System.out.println(HexDump.hexDump(b)); b.put(new byte[]{0x61,0x61}); b.flip(); System.out.println(HexDump.hexDump(b));
什么情况下会抛出IndexOutOfBoundsException
,这个应该很简单,你的缓冲区的大小只分配了1个字节,你如果插入两个字节就会报出这样的错误。
2.线程安全
在jdk.doc里,明确告诉你Buffer类(ByteBuffer继承自它)不是线程安全的。大白话就是,当多个线程同时调用同一个buffer的时候,数据会出错。这个问题可是之前困扰我很久。现在想想,就想说TMD。
好吧,附上测试代码
package test; import java.nio.ByteBuffer; import cn.hexing.fk.utils.HexDump; public class TestByteBuffer { public static void main(String[] args) { Thread1 t = new Thread1(); Thread1 t2 = new Thread1(); TestMessage1 message = new TestMessage1(); t.message = message; t2.message = message; t.start(); t2.start(); } } class Thread1 extends Thread{ public TestMessage1 message; @Override public void run(){ for(int i = 0 ; i < 10000 ; i++){ ByteBuffer buffer=ByteBuffer.allocateDirect(1000); message.write(buffer); buffer.flip(); String str=HexDump.hexDump(buffer); System.out.println(str); if(str.equals("00 01 00 01 00 01 00 1C CC 1A 30 00 00 0C 68 D3 D3 98 EA 8F 7D 95 DA 46 6F F9 5E B1 DD FC 59 12 51 5B 99 12")){ System.out.println("sdf"); }else{ System.out.println("nonon"); } } } } class TestMessage1 { ByteBuffer apdu = HexDump.toByteBuffer("CC1A3000000C68D3D398EA8F7D95DA466FF95EB1DDFC5912515B9912"); private short version = 0x0001; private short srcAddr = 0x0001; private short dstAddr = 0x0001; public void write(ByteBuffer buffer){ synchronized (this) { buffer.putShort(version); buffer.putShort(srcAddr); buffer.putShort(dstAddr); buffer.putShort((short) apdu.remaining()); while(apdu.hasRemaining() && buffer.hasRemaining()) buffer.put(apdu.get()); apdu.position(0); } } }
上面的代码是没有问题的,就是两个线程拥有一个message,同时调用write方法,由于在write方法里加了synchronized,所以是线程安全的,如果将synchronized去掉的话,输出会出现"nono"哦。