如何在多线程环境中正确使用ByteBuffer?

时间:2016-05-02 03:54:45

标签: java multithreading

在一个数据类 A类中,我有以下内容:

class A
{
    private byte[] coverInfo = new byte[CoverInfo.SIZE];

    private ByteBuffer coverInfoByteBuffer = ByteBuffer.wrap(coverInfo);
    ...
}

CoverInfo类中,我的字段很少:

class CoverInfo
{    
    public static final int SIZE = 48;

    private byte[] name = new byte[DataConstants.Cover_NameLength];
    private byte[] id = new byte[DataConstants.Cover_IdLength];
    private byte[] sex = new byte[DataConstants.Cover_SexLength];
    private byte[] age = new byte[DataConstants.Cover_AgeLength];

}

A类获取coverInfo数据时,我创建了一个CoverInfo实例,并将数据填充到CoverInfo对象中,如 inside A类:

public void createCoverInfo()
{
    CoverInfo tempObj = new CoverInfo();
    tempObj.populate(coverInfoByteBuffer);
    ....
}

CoverInfo 类的populate()方法中,我有以下内容:

public void populate(ByteBuffer dataBuf)
{
    dataBuf.rewind();

    dataBuf.get(name, 0, DataConstants.Cover_NameLength);
    dataBuf.get(id, 0, DataConstants.Cover_IdLength);
    dataBuf.get(sex, 0, DataConstants.Cover_SexLength);
    dataBuf.get(age, 0, DataConstants.Cover_AgeLength);
}

populate()方法将在Windows上抛出异常(始终),但它适用于Linux:

java.nio.BufferUnderflowException
    java.nio.HeapByteBuffer.get(HeapByteBuffer.java:151)
    com.bowing.uiapp.common.socketdata.message.out.CoverInfo.populate(CoverInfo.java:110)

并且异常行号未固定在一行中。

它正在多线程环境上运行。

如果我使用重复的(只读是好的)ByteBuffer,问题就解决了:

tempObj.populate(coverInfoByteBuffer.duplicate());

关于此的几个问题:

  1. 为什么它适用于Linux但不适用于Windows(只是时间问题)?
  2. 我猜这个问题是由于当这个CoverInfo对象访问ByteBuffer时其他人更改了限制/位置/标记值,复制()是这种情况的首选方式吗?
  3. 如果使用ByteBuffer的slice(),如果有多个用户修改ByteBuffer,如何保证数据的完整性?
  4. 如何在多线程环境中正确使用ByteBuffer?

2 个答案:

答案 0 :(得分:2)

来自Buffer类的Javadoc

  

线程安全

     

多个并发线程使用缓冲区是不安全的。如果要由多个线程使用缓冲区,则应通过适当的同步来控制对缓冲区的访问。

这就是规范所说的。正如您所说,使用自己的独立位置等创建缓冲区的多个视图可以正常工作。此外,使用绝对读取(您指定位置的位置)也可能有效。这些都不能保证根据文档工作,并且可能仅适用于某些缓冲区实现。

答案 1 :(得分:1)

我猜测问题是你有多个线程同时尝试在缓冲区上工作,尽管它们都没有修改缓冲区中的数据,它们正在改变缓冲区的状态,特别是读/写位置。

解决方案:

一次只允许一个线程与缓冲区进行交互...

public void populate(ByteBuffer dataBuf)
{
    synchronized(dataBuf){
        dataBuf.rewind();

        dataBuf.get(name, 0, DataConstants.Cover_NameLength);
        dataBuf.get(id, 0, DataConstants.Cover_IdLength);
        dataBuf.get(sex, 0, DataConstants.Cover_SexLength);
        dataBuf.get(age, 0, DataConstants.Cover_AgeLength);
    }
}

OR

为每个人创建一个新的ByteBuffer。

public void populate(ByteBuffer dataBuf)
{
    ByteBuffer myDataBuf = dataBuf.asReadOnlyBuffer();
    myDataBuf.get(name, 0, DataConstants.Cover_NameLength);
    myDataBuf.get(id, 0, DataConstants.Cover_IdLength);
    myDataBuf.get(sex, 0, DataConstants.Cover_SexLength);
    myDataBuf.get(age, 0, DataConstants.Cover_AgeLength);
}