Java 同步循环队列

Java 同步循环队列,java,thread-safety,queue,deadlock,synchronized,Java,Thread Safety,Queue,Deadlock,Synchronized,我正在实现一个非常简单的同步循环队列,如下所示,我的一个朋友说它容易出现死锁!但我不这么认为 实际上,如果队列为空,则当一个线程想要出列(轮询)时,它必须等待另一个线程加入(提供)一个元素,反之亦然,如果队列已满 我不太擅长寻找容易死锁的代码,你认为它也容易死锁吗 import java.util.ArrayList; class CircularQueue<T>{ ArrayList<T> q; int front , rear , size;

我正在实现一个非常简单的同步
循环队列
,如下所示,我的一个朋友说它容易出现
死锁
!但我不这么认为

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

我不太擅长寻找容易死锁的代码,你认为它也容易死锁吗

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;
     }
}
import java.util.ArrayList;
类循环队列{
ArrayList q;
内景前,后,尺寸;
公共循环队列(整数大小){
q=新的ArrayList();
对于(int i=0;i
您需要同步整个方法,而不仅仅是wait()调用

想象一下,如果两个线程都试图同时提供()时,剩下两个插槽,会发生什么情况。它们都可以通过同步块,然后在下一行读取rear的不同值。然后,对poll()的下一个调用将被阻止,但该值已经存在


这段代码还存在一些其他问题:您无法处理虚假唤醒,并且在不按住监视器的情况下在外部调用notify()。此外,在这里我将使用Object[]而不是ArrayList,因为固定大小的可变集合正是您所需要的。

您的实现有几个问题:

  • notify()
    的调用必须来自
    synchronized
    块内部
  • 您的实现创建了“延迟器”——一种Java内存泄漏,当对象被阻止收集的时间超过其应该收集的时间时。要解决此问题,请将从
    poll()
    返回的元素设置为
    null
  • 您不需要使用
    ArrayList
    并用
    null
    s填充它;一个简单的
    对象数组
    就足够了。您可能需要添加强制转换,但不管有无
    ArrayList
    ,它都会出现,所以您最好将其移动到代码中

最后一点允许队列的恶意用户通过在队列对象本身上进行同步而不释放锁来永久性地暂停进程。

This.notify()
将始终抛出
IllegalMonitorStateException
,因为它不是同步的。可能我丢失了一些东西,但这与一个错误不完全相同吗?您的实现未同步,导致意外行为。例如,在
poll()
方法中,两个线程可能会同时更改
front
。@Adam Siemion:这是因为可能没有线程在等待吗?对
的同步有不同意见:在像这样的低级实用程序中,我认为这不是问题。这个类的用户应该总是私下实例化它,这样其他人就不能锁定它。关于你的第三个注意事项,但是实例化一个通用数组是不可能的!是吗?@ArianHosseinzadeh:只需创建一个擦除类型的数组,在本例中是Object。由于数组协方差,它将起作用,但您将得到一个必须抑制的警告。@ArianHosseinzadeh或甚至
Object[]q=new Object[size]()
T result=(T)q[front]@ArianHosseinzadeh:是的。您还需要一个
@SuppressWarnings(“unchecked”)
来告诉编译器您知道自己在做什么。或者,正如dasblinkenlight所说,在访问点进行强制转换。那么你的意思是我必须使这两种方法(轮询和提供)同步吗?那我还得等什么?因为正如我所说的,我想等到有人将某个东西排到队列中(在排队列的情况下),在检查条件的同时,您需要等待()并在循环中超时。不一定要同步整个方法,但需要同步读-修改-写操作,这样就不必与其他线程竞争。为此,您可能会发现AtomicInteger类非常方便。