同步循环队列

时间:2013-06-21 16:25:24

标签: java thread-safety queue deadlock synchronized

我正在实施一个非常简单的同步Circular Queue,我的一位朋友说它很容易deadlock!但我不相信,

实际上当一个线程想要出队(轮询)队列为空时,它必须等到另一个线程入队(提供)一个元素,反之亦然,如果队列已满,

我不是很擅长发现容易出现死锁的代码,你认为它也容易出现死锁吗?

import java.util.ArrayList;

class CircularQueue<T>{
    ArrayList<T> q;
    int front , rear , size;
    public CircularQueue(int size){
        q = new ArrayList<T>();
        for (int i = 0 ; i < size ; i++)
            q.add(null);
        front =  0;
        rear =0;
        this.size = size;
     }
     public void offer(T t) throws InterruptedException{
        synchronized(this){
            if ( (rear + 1) % size == front)
                this.wait();    
        }
        rear = (rear + 1) % size;
        q.set(rear, t);
        this.notify();
     }
     public T poll() throws InterruptedException{
        synchronized(this){
            if (rear == front)
                this.wait();
        }
        front = (front+1) % size;
        T result = q.get(front);
        this.notify();
        return result;
     }
}

2 个答案:

答案 0 :(得分:1)

您的实施存在以下几个问题:

  • notify()的来电必须来自synchronized区块
  • 您的实现会创建“lingerers” - 一种Java内存泄漏,阻止对象被收集的时间超过应有的时间。要解决此问题,请将您从poll()返回的元素设置为null
  • 您无需使用ArrayList<T>并将其填入null s;一个简单的Object阵列就足够了。您需要添加转换,但无论如何,无论有没有ArrayList,它都会存在,所以您不妨将它移动到您的代码中。
  • You should not synchronize on this

最后一点允许队列的恶意用户通过同步队列对象本身而不是释放锁来永久停止进度。

答案 1 :(得分:0)

您需要同步整个方法,而不仅仅是wait()调用。

想象一下,如果两个线程同时尝试提供(),当剩下两个插槽时会发生什么。它们都可以通过同步块,然后在下一行读取后面的不同值。然后下一次调用poll()会阻塞,但值已经存在。

此代码还存在一些其他问题:您不处理虚假唤醒,而是在不保持监视器的情况下调用notify()。此外,我会在这里使用Object []而不是ArrayList,因为固定大小的可变集合正是你想要的。