如果队列已满,ArrayBlockingQueue将阻塞生产者线程,如果队列为空,它将阻塞使用者线程。
这种阻塞概念不符合多线程的想法吗?如果我有一个'主'线程,让我们说我想将所有'Logging'活动委托给另一个线程。所以基本上在我的主线程中,我创建一个Runnable来记录输出,并将Runnable放在ArrayBlockingQueue上。这样做的全部目的是让'main'线程立即返回,而不会浪费任何时间进行昂贵的日志记录操作。
但是如果队列已满,主线程将被阻止,并将等待一个点可用。那么它对我们有什么帮助?
答案 0 :(得分:9)
队列不会被阻止,它会阻止在系统中引入额外的质量。在这种情况下,它是防止饥饿。
画出一组线程,其中一个线程非常快速地生成工作单元。如果允许队列无限制增长,那么“快速生产者”队列可能会占用所有生产能力。有时,预防这种副作用比使所有线程解除阻塞更重要。
答案 1 :(得分:5)
我认为这是设计师的决定。如果他选择阻塞模式,ArrayBlockingQueue会为其提供put
方法。如果设计者不想要阻塞模式,则ArrayBlockingQueue具有offer
方法,该方法在队列已满时将返回false,但随后他需要决定如何处理重新记录的日志记录事件。
答案 2 :(得分:3)
在您的示例中,我会将阻塞视为一个功能:它可以防止OutOfMemoryError。
一般来说,你的一个线程速度不够快,无法应对分配的负载。所以其他人必须以某种方式放慢脚步,以免危及整个申请。
另一方面,如果负载平衡,则队列不会阻塞。
答案 3 :(得分:2)
阻塞是多线程的必要功能。您必须阻止才能同步访问数据。它并没有破坏多线程的目的。
我建议在生产者尝试将项目提交到已满的队列时抛出异常。有些方法可以预先测试容量是否已满。
这将允许调用代码决定它如何处理完整队列。
如果从队列处理项目时的执行顺序不重要,我建议使用线程池(在Java中称为ExecutorService)。
答案 4 :(得分:1)
这取决于多线程理念的本质。对于我们这些喜欢Communicating Sequential Processes的人来说,阻塞队列几乎是完美的。实际上,除非接收器准备接收消息,否则理想情况是根本不能将消息放入队列。
所以不,我不认为阻塞队列违背了多线程的目的。实际上,您描述的场景(主线程最终停滞不前)是多线程的actor-model主要问题的一个很好的例证。你不知道它是否会死锁/阻塞,你也无法对其进行详尽的测试。
相比之下,想象一个阻塞队列,它是零消息深度。对于系统工作的方式,你必须找到一种方法来确保记录器始终能够从主线程接收消息。那是CSP。这可能意味着在您的假设记录器线程中,您必须具有应用程序定义的缓冲(而不是某些框架开发人员对FIFO应该有多深的最佳猜测),快速I / O子系统,检查是否跟上,处理方式落后等等。简而言之,它不会让你逃脱它,你被迫解决系统性能的各个方面。
这当然更难,但是这样你最终会得到一个绝对可行的系统,而不是如果你的阻塞队列是一个未知数量的消息,你可能会遇到可疑的“可能”。
答案 5 :(得分:0)
听起来你有一般的想法,为什么你会使用类似ArrayBlockingQueue
之类的东西在线程之间进行交谈。
拥有阻塞队列可让您选择在后台工作线程出现问题时执行不同的操作,而不是盲目地向队列添加更多请求。如果队列中有空间,则没有阻塞。
对于您的特定用例,我会使用ExecutorService
而不是直接读/写队列,这会创建一个后台工作线程池:
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html
pool = Executors.newFixedThreadPool(poolSize);
pool.submit(myRunnable);
答案 6 :(得分:0)
答案 7 :(得分:0)
多线程程序是非确定性的,因为您事先无法说明:n生产者操作将花费与m个消费者操作完全相同的时间。因此,在每种情况下都需要n个生产者和消费者之间的同步。
您需要选择队列大小,以便在大多数情况下最大化活动生产者和使用者的数量。但java的线程模型并不保证任何使用者都会运行,除非它是唯一的未阻塞线程。 (当然,在多核CPU上,消费者很可能会运行)。