使用ConcurrentLinkedQueue的Java线程问题

| 我的以下代码片段有问题。它旨在处理添加到事件队列(ConcurrentLinkedQueue)中的事件(通过对processEvent方法的调用提供)。将事件添加到事件队列中,并在run方法中对其进行定期处理。 几乎总是一切都很好。但是有时在调用processEvent方法之后,将事件添加到队列时,运行部分无法看到有新事件。 有什么问题的主意吗?除了使用String常量作为锁的明显错误之外?
import java.util.concurrent.ConcurrentLinkedQueue;

public class MyCommunicator implements Runnable {

private ConcurrentLinkedQueue<MyEvent> eventQueue = null;

private boolean stopped = false;

private String lock = \"\";
private Thread thread = null;

public MyCommunicator() {

    eventQueue = new ConcurrentLinkedQueue<MyEvent>();
}

public void start() {
    thread = new Thread(this, \"MyCommunicatorThread\");
    thread.start();
}

public void stop() {
    stopped = true;
    synchronized (lock) {
        lock.notifyAll();
    }
    eventQueue.clear();
}

public void run() {
    while (!stopped) {
        try {

            MyEvent event = null;
            while (!stopped && ((event = eventQueue.peek()) != null)) {
                sendEvent(event);
                eventQueue.poll();
            }

            if (!stopped) {
                synchronized (lock) {
                    lock.wait(10000L);
                }
            }
        }

        catch (Exception e) {

        }
    }
}

/**
 * START EVENT JOB - ADD A NEW EVENT TO BE PROCESSED
 */
public void processEvent(MyEvent event) {
    eventQueue.offer(event);
    synchronized (lock) {
        lock.notifyAll();
    }
}

/**
 * END EVENT JOB
 */
private void sendEvent(MyEvent event) {
    // do send event job
}

}
    
已邀请:
您有所谓的错过信号。您轮询队列,然后在监视器上等待(获取锁定)。生产者线程添加事件,然后调用ѭ1(获取锁)。事件排队/轮询与条件等待/通知之间没有“ 2”关系。 因此,线程A可能会在空时进行轮询,然后尝试获取该锁,同时线程B添加一个元素并获取该锁,通知所有正在等待的线程,然后释放该锁。然后线程A获取锁并等待它,但是信号已丢失。 当您仅将锁用于信令时,您可能会考虑另一种机制,例如像Doug Lea的新jdk7 Phaser这样的可重用闩锁,或者直接使用
BlockingQueue
。 或者,我们有几个ReusableLatch,例如用于单个读取器线程的BooleanLatch或用于多方支持的PhasedLatch。     
为什么要使用锁和通知? 改用LinkedBlockingQueue并节省所有麻烦。 ѭ4上的超时将完成您尝试做的所有事情。 编辑:关于当前代码; 您将需要定义“无法看到新事件”。您的
run()
方法每10秒查看一次队列;如果队列中有东西,它将“看到”并拉出。 如果您的意思是“收到通知后不会立即看到它,仅10秒后就可以看到”,那么这很容易回答,因为您的比赛条件很容易导致这种情况发生。在此线程介于完成检查/处理队列和获取锁之间的期间,可以在队列中插入某些内容。如果在ѭ6上没有超时,您将阻塞直到插入下一个事件。如果在这段时间内正在调用
stop()
方法,则您将丢失队列中的所有事件。使用
LinkedBlockingQueue
代替所有不必要的锁定和通知即可解决此问题。这不是一个“简单”的解决方案,它是针对此用例和问题的正确解决方案。 如果不是这种情况,那么您根本就没有在队列中插入任何内容,问题出在您未在此处发布的代码中。一个不知道该代码的猜测可能是您试图在ѭ10处插入一个空ѭ9。由于您没有尝试/抓住11英镑,因此您不会知道。忽略所有异常而不检查返回的值既不是一个好主意,也不是一种实践。 第三种可能性是,您在其他某个位置上锁定了相同的确切字符串字面量引用,从而导致该代码挂起。您提到了它,但我会在这里重申-这确实是一件坏事,特别是考虑到它是空字符串。如果您坚持在此处使用
java.util.concurrent
软件包,则可以提供带有条件的实锁。请注意,这仍然无法消除您有时会错过10秒比赛的竞争状况,但至少会更加干净。为了消除竞争状况,您希望放弃并发队列的常规队列,并在访问它之前先获取该锁(以及获取插入的锁)。这将使您的线程同步,因为除非该线程正在等待锁定条件,否则将阻止插入该插入器。在同一代码段中混合使用锁和无锁方法来进行线程同步通常会导致这些问题。     
乍一看并没有什么特别的主意,但是由于以下原因,许多事情可能会出错:
    catch (Exception e) {

    }
捕获任何ѭ14(包括ѭ15及其各种子类)然后忽略它的处理程序通常是一个坏主意。如果这是为了捕获特定类型的异常(例如,可能由
lock.wait()
抛出的
InterruptedException
),则应将其限制为该异常类型。如果您有捕获任何异常的某种原因,那么至少应在发生异常时记录一些内容。     
我对ѭ18遇到的一个问题我真的怀疑是真正的错误,因为它并不是真正的完全同步的证据。 我还没有完全测试过,但是我看了一下代码,如果队列实际上为空,我很确定.isEmpty()不同步。当一个线程调用.isEmpty()并返回“ 19”时,队列可能已经包含元素。     

要回复问题请先登录注册