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

时间:2016-11-09 17:04:56

标签: java multithreading wait

情景:

来自不同来源的多线程阅读 共享队列的一个单一访问点(请参阅尝试写入的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&lt;容量,我决定只通知一个线程,因为恕我直言,此时,没有线程应处于状态等待状态。我错了吗?我应该通知All吗?

2b)“else”语句的确切问题。 notifyAll是一个好习惯吗?在这一点上,可能所有人都在等待?

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

2 个答案:

答案 0 :(得分:1)

我会尽力逐一回答你的问题。

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

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

3)可以通知所有人。如果线程数超过容量,则等待其余部分快速进入wait状态。

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

答案 1 :(得分:1)

  

1)读者线程只能模糊地使用addRecord?他们会去吗?   等自己?或者我是否必须实施其他方法   在运行addRecord方法之前检查哪里?

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

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

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

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

  

2)当queue.size&lt;容量,我决定只通知一个线程,   因为恕我直言,此时,没有线程应处于状态等待状态。   我错了吗?我应该通知All吗?

您的帖子只能等待queue.size() >= capacity,所以对我来说notify甚至不需要,因为任何线程都不希望这个条件(queue.size() < capacity)。

  

2b)“else”语句的确切问题。这是一个好习惯吗?   notifyAll的?在这一点上,可能所有人都在等待?

来自Effective Java 的第69项:

  

相关问题是您是应该使用notify还是notifyAll来唤醒   等待线程。 (回想一下,notify唤醒了一个等待的线程,   假设存在这样的线程,并notifyAll唤醒所有等待   线程。)通常说你应该总是使用notifyAll。这个   是合理的,保守的建议。它总是会产生正确的   结果,因为它保证你将唤醒所需的线程   被唤醒你也可以唤醒其他一些线程,但事实并非如此   影响程序的正确性。这些线程将检查   他们等待的条件,并且发现它是假的   继续等待。作为优化,您可以选择调用notify   如果所有可能在等待集中的线程都是,则代替notifyAll   等待相同的条件,一次只能有一个线程   受益于条件成真。即使这些条件   看似正确,可能有理由使用notifyAll代替通知。   就像在循环中放置等待调用一样可以防范   对可公开访问的对象的意外或恶意通知,   使用notifyAll代替notify可以防止意外或   恶意等待一个不相关的线程。否则这样的等待   “吞下”一个关键通知,留下其预定的收件人   无限期地等待。

  

3)最后。我有点担心使用Lock e重写所有内容   条件类。这是一个更好的决定吗?或者我是怎么回事   运行这种情况?

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