情景:
来自不同来源的多线程阅读
共享队列的一个单一访问点(请参阅尝试写入的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重写所有内容。这是一个更好的决定吗?或者我是如何运行这种情况的?
答案 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重写所有内容 条件类。这是一个更好的决定吗?或者我是怎么回事 运行这种情况?
Lock
和Condition
非常有趣,如果您需要内部锁定不具备的功能(例如tryLock()
)或仅唤醒等待给定条件的线程的功能。在你的情况下,它似乎没有必要,所以你可以保持原样。