最近在项目中,频繁使用到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"哦。