`

ByteBuffer使用有感

    博客分类:
  • java
阅读更多

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

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics