列出并发失败

时间:2013-10-30 19:09:22

标签: java android multithreading list concurrency

我有一个Arraylist,我不断添加和删除单独的线程。 一个线程添加,另一个删除。

这是包含更改列表的类:

public class DataReceiver {
    private static final String DEBUG_TAG = "DataReceiver";

    // Class variables
    private volatile ArrayList<Byte> buffer;
    //private volatile Semaphore dataAmount;

    public DataReceiver() {
        this.buffer = new ArrayList<Byte>();
        //this.dataAmount = new Semaphore(0, true);
    }

    // Adds a data sample to the data buffer. 
    public final void addData(byte[] newData, int bytes) {
        int newDataPos = 0;

        // While there is still data
        while(newDataPos < bytes) {
            // Fill data buffer array with new data
            buffer.add(newData[newDataPos]);
            newDataPos++;
            //dataAmount.release();
        }

        return;
    }

    public synchronized byte getDataByte() {
/*
        try {
            dataAmount.acquire();
        }
        catch(InterruptedException e) {
            return 0;
        }
*/
        while(buffer.size() == 0) {
            try {
                Thread.sleep(250);
            }
            catch(Exception e) {
                Log.d(DEBUG_TAG, "getDataByte: failed to sleep");
            }
        }

        return buffer.remove(0);
    }
}

问题是我在尝试buffer.remove(0)时经常遇到一个空指针。正如您可以在代码中告诉表单中的注释,我尝试在某一点使用信号量,但它仍然间歇地抛出nullpointer异常,因此我创建了自己的sleep-poll类型作为概念的半证明。

我不明白为什么会出现空指针异常和/或如何修复它。

2 个答案:

答案 0 :(得分:1)

如果你在另一个线程中处理对象初始化,那么构造函数可能在

之前没有完成
 public synchronized byte getDataByte() 
因此调用

导致NullPointerException因为

this.buffer = new ArrayList<Byte>(); 

从未被召唤过。

答案 1 :(得分:0)

我猜到了一个解释。我会在评论中这样做,但我没有足够的声誉,所以希望这个答案是有帮助的。

首先,如果你要将addData()函数声明为synchronized,你的问题会消失吗?我的猜测是它会。

我的理论是,尽管您将缓冲区声明为volatile,但这并不足以保护您的用例。想象一下这个案例:

  • 调用addData()并调用buffer.add()
  • 同时,getDataByte()正在检查buffer.size()== 0

我的理论是buffer.add()不是原子操作。在buffer.add()操作期间的某个地方,它的内部大小计数器递增,使得对buffer.size()== 0的getDataByte()调用返回false。有时,getDataByte()会在buffer.add()调用完成之前继续调用buffer.remove()。

这是基于我在这里阅读的摘录: https://www.ibm.com/developerworks/java/library/j-jtp06197/ “虽然递增操作(x ++)可能看起来像一个操作,但它实际上是一个必须以原子方式执行的复合读 - 修改 - 写操作序列 - 而且volatile不提供必要的原子性。”