访问共享资源,锁定解锁或等待通知

Access to shared resource, lock unlock or wait notify

场景:

多线程从不同来源读取。
共享队列的一个访问点(参见 class RiderSynchronized 尝试写入)
Reader 读取的每一行,它都会尝试通过 RiderSynchronized 提供的方法插入共享队列。

当共享队列已满时,我必须运行 将准备好的语句批量插入到 Oracle 中。同时,必须拒绝所有对共享队列的访问。

代码:

public class RiderSynchronized {

    private ArrayDeque<JSONRecord> queue = new ArrayDeque<>();
    private OracleDAO oracleDao;
    private long capacity;

    public RiderSynchronized(OracleDAO oracleDao, long capacity) {
        this.oracleDao = oracleDao;
        this.capacity = capacity;
    }

    public synchronized boolean addRecord(JSONRecord record) {
        boolean success = false;
        try {
            while (queue.size() >= capacity) {
                wait();
            }

            queue.add(record);
            if (queue.size() < capacity) {
                success = true;
                notify(); //notify single Thread
            } else {
                JSONRecord currentRecord = null;
                while ((currentRecord = queue.poll()) != null) {
                    oracleDao.insertRowParsedIntoBatch(currentRecord);
                }
                oracleDao.runBatch();
                success = true;
                notifyAll(); //it could be all Reading Threads are waiting. Notify all

            }

        } catch (Exception e) {
            success = false;
        }
        return success;
    }
}

我不得不承认我有点担心一件事。

1) Reader 线程只能模糊地使用addRecord?他们要等自己吗?或者我是否必须在 运行 addRecord 方法之前实现一些其他方法来检查?

2) 当queue.size < 容量时,我决定只通知一个线程,因为恕我直言,在这一点上,没有线程应该处于等待状态。我错了吗?我应该通知所有人吗?

2b) "else" 语句的确切问题。 notifyAll 是个好习惯吗?此时,可能是所有线程都在等待?

3) 最后。我有点担心使用 Lock e Condition 类 重写所有内容。这是一个更好的决定吗?或者我如何 运行 处理这种情况可以吗?

我会尽量一一为您解答。

1) 如果我理解正确,答案是 线程将等待,您不需要做任何其他事情。

2) 如果queue.size < capacity此时没有等待线程,则无需通知任何人。

3) 是的通知大家就可以了。如果等待的线程多于容量,则其余线程会快速进入 wait 状态。

4) 这是基于意见的问题。在您的场景中,您不会从重写中获得任何好处。

1) Reader threads can just use addRecord indistinctly? Are They going to wait for themselves? Or Do I have to implement some other method where to check before to run addRecord Method?

您当前代码的问题在于,如果由于某种原因 notifyAll 未被理论上应该能够进入 else 块的唯一线程调用,则 你的线程将永远等待.

您的代码中的潜在风险是:

  • oracleDao.insertRowParsedIntoBatch(currentRecord)
  • oracleDao.runBatch()

使用您当前的代码,如果其中一个方法抛出异常 notifyAll 将永远不会被调用,因此 您的线程将永远等待 ,您至少应该考虑调用 notifyAllfinally 块中以确保它会被调用是否发生。


2) When queue.size < capacity, I decide to notify just to one thread, because IMHO, at this point, no threads should be in status waiting. Am I wrong? Should I notify All?

你的线程只能等待以防万一 queue.size() >= capacity 所以对我来说 notify 甚至不需要,因为任何线程都不需要这种情况 (queue.size() < capacity)。


2b) Exact question for the "else" statement. Is it a good practice to notifyAll? At this point, it could be all threds are waiting?

来自 Effective Java 的项目 69:

A related issue is whether you should use notify or notifyAll to wake waiting threads. (Recall that notify wakes a single waiting thread, assuming such a thread exists, and notifyAll wakes all waiting threads.) It is often said that you should always use notifyAll. This is reasonable, conservative advice. It will always yield correct results because it guarantees that you’ll wake the threads that need to be awakened. You may wake some other threads, too, but this won’t affect the correctness of your program. These threads will check the condition for which they’re waiting and, finding it false, will continue waiting. As an optimization, you may choose to invoke notify instead of notifyAll if all threads that could be in the wait-set are waiting for the same condition and only one thread at a time can benefit from the condition becoming true. Even if these conditions appear true, there may be cause to use notifyAll in place of notify. Just as placing the wait invocation in a loop protects against accidental or malicious notifications on a publicly accessible object, using notifyAll in place of notify protects against accidental or malicious waits by an unrelated thread. Such waits could otherwise “swallow” a critical notification, leaving its intended recipient waiting indefinitely.


3) Finally. I'm a little concerned to re-write everything using Lock e Condition Classes. Is it a better decision? Or Is it ok how I'm running this scenario?

LockCondition 如果您需要内在锁不可用的功能(例如 tryLock() 或仅唤醒等待给定条件的线程的能力),则它们很有趣。在你的情况下,它似乎没有必要,所以你可以保持原样。