繁体   English   中英

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

[英]Access to shared resource, lock unlock or wait notify

场景:

从不同来源读取多线程。
一个共享队列的单个访问点(请参阅类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)阅读器线程可以只是模糊地使用addRecord吗? 他们要等自己吗? 还是我必须在运行addRecord方法之前在其他地方实现其他方法?

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

2b)“其他”陈述的确切问题。 notifyAll是一个好习惯吗? 在这一点上,可能是所有的烦恼都在等待吗?

3)最后。 我有点担心要使用Lock e Condition Classs重新编写所有内容。 这是一个更好的决定吗? 还是可以在这种情况下运行?

我会尽力回答您的问题。

1)如果我理解正确,答案是是,线程将等待,您无需执行其他任何操作。

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

3) 是的 ,可以通知所有人。 如果正在等待的线程多于容量,则其余线程将很快进入wait状态。

4)是基于意见的问题。 在您的方案中,重写不会带来任何好处。

1)阅读器线程可以只是模糊地使用addRecord吗? 他们要等自己吗? 还是我必须在运行addRecord方法之前在其他地方实现其他方法?

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

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

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

根据您当前的代码,如果这些方法之一抛出异常notifyAll永远不会被调用,所以你的线程将永远等待 ,你至少应该考虑调用notifyAllfinally块,以确保它会被称为是否发生。


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

您的线程只能在queue.size() >= capacity情况下等待,因此对我来说,甚至不需要notify ,因为任何线程都不会期望此条件( queue.size() < capacity


2b)“其他”陈述的确切问题。 notifyAll是一个好习惯吗? 在这一点上,可能是所有的烦恼都在等待吗?

来自有效Java的第69项

一个相关的问题是您应该使用notify还是notifyAll来唤醒等待的线程。 (回想一下,假定存在一个这样的线程, notify唤醒一个正在等待的线程,而notifyAll唤醒所有正在等待的线程。)通常说您应该始终使用notifyAll 这是合理的,保守的建议。 它将始终产生正确的结果,因为它可以确保您唤醒需要唤醒的线程。 您也可能会唤醒其他线程,但这不会影响程序的正确性。 这些线程将检查它们正在等待的条件,如果发现错误,将继续等待。 作为一种优化,如果可能在等待集中的所有线程都在等待同一条件,并且一次仅一个线程可以从条件变为真中受益,则可以选择调用notify而不是notifyAll 即使这些条件为真,也可能会导致使用notifyAll代替notify。 就像将等待调用置于循环中可以防止对可公开访问的对象发出意外或恶意通知一样,使用notifyAll代替notify可以防止无关线程意外或恶意等待。 否则,此类等待可能“ 吞噬 ”关键通知,从而使预期的接收者无限期地等待。


3)最后。 我有点担心要使用Lock e Condition Classs重新编写所有内容。 这是一个更好的决定吗? 还是可以在这种情况下运行?

如果您需要固有锁不具备的功能tryLock()例如tryLock()或仅唤醒等待给定条件的线程的功能),则LockCondition很有意思。 在您的情况下,似乎没有必要,因此可以按原样保留它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM