Java 使用newFixedThreadPool的多线程程序不';当线程池大小小于已执行的任务数时,不能作为例外运行

Java 使用newFixedThreadPool的多线程程序不';当线程池大小小于已执行的任务数时,不能作为例外运行,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,我在这里做错了什么?要修复该行为,您需要执行以下操作之一: 具有足够的可运行程序,每个可运行程序都具有足够的队列容量以容纳所有1000条消息(例如:100个可运行程序,容量为10条或更多;或10个可运行程序,容量为100条或更多),或 有一个足够大的线程池来容纳所有可运行的线程,这样每个线程都可以开始运行 如果没有这些情况,ExecutorService将不会启动额外的可运行程序。主工作线程将继续向每个队列添加项目,包括非运行可运行程序的项目,直到遇到一个已满的队列,此时它将阻塞该队列。有1

我在这里做错了什么?

要修复该行为,您需要执行以下操作之一:

  • 具有足够的可运行程序,每个可运行程序都具有足够的队列容量以容纳所有1000条消息(例如:100个可运行程序,容量为10条或更多;或10个可运行程序,容量为100条或更多),或
  • 有一个足够大的线程池来容纳所有可运行的线程,这样每个线程都可以开始运行
如果没有这些情况,ExecutorService将不会启动额外的可运行程序。主工作线程将继续向每个队列添加项目,包括非运行可运行程序的项目,直到遇到一个已满的队列,此时它将阻塞该队列。有10个可运行线程,线程池大小为5,要填充的第一个队列将是第6个可运行线程。如果您只有6个Runnables,这是相同的。重要的一点是,至少比线程池中的空间多了一个Runnable

发件人:

如果在所有线程都处于活动状态时提交其他任务,它们将在队列中等待,直到有线程可用

考虑2个进程和线程池大小为1的简单示例。您可以创建第一个进程并将其提交给ExecutorService(以便ExecutorService启动并运行它)。但是,ExecutorService不允许运行第二个进程。但是,您的主线程没有注意到这一点,它将继续将元素放入第二个进程的队列中,即使没有任何东西在使用它

如果您还将队列大小更改为100,则代码可以使用
noOfProcess=10
线程池大小=5
,如下所示:
newlinkedblockingqueue(100)

如果更改此行,您可以观察到此行为–非运行Runnable的队列已满:

processes.get(roundRobinIndex++).getElements().put(i);
对此(这是相同的逻辑代码,但保存了对象引用以供在
println()
输出中使用):

MyRunnable=processs.get(roundRobinIndex++);
BlockingQueue elements=runnable.getElements();
System.out.println(“尝试将“+runnable.getTaskName()+”的put()与“+elements.size()+”elements一起放置”);
要素。付诸表决(i);

要修复该行为,您需要执行以下操作之一:

  • 具有足够的可运行程序,每个可运行程序都具有足够的队列容量以容纳所有1000条消息(例如:100个可运行程序,容量为10条或更多;或10个可运行程序,容量为100条或更多),或
  • 有一个足够大的线程池来容纳所有可运行的线程,这样每个线程都可以开始运行
如果没有这些情况,ExecutorService将不会启动额外的可运行程序。主工作线程将继续向每个队列添加项目,包括非运行可运行程序的项目,直到遇到一个已满的队列,此时它将阻塞该队列。有10个可运行线程,线程池大小为5,要填充的第一个队列将是第6个可运行线程。如果您只有6个Runnables,这是相同的。重要的一点是,至少比线程池中的空间多了一个Runnable

发件人:

如果在所有线程都处于活动状态时提交其他任务,它们将在队列中等待,直到有线程可用

考虑2个进程和线程池大小为1的简单示例。您可以创建第一个进程并将其提交给ExecutorService(以便ExecutorService启动并运行它)。但是,ExecutorService不允许运行第二个进程。但是,您的主线程没有注意到这一点,它将继续将元素放入第二个进程的队列中,即使没有任何东西在使用它

如果您还将队列大小更改为100,则代码可以使用
noOfProcess=10
线程池大小=5
,如下所示:
newlinkedblockingqueue(100)

如果更改此行,您可以观察到此行为–非运行Runnable的队列已满:

processes.get(roundRobinIndex++).getElements().put(i);
对此(这是相同的逻辑代码,但保存了对象引用以供在
println()
输出中使用):

MyRunnable=processs.get(roundRobinIndex++);
BlockingQueue elements=runnable.getElements();
System.out.println(“尝试将“+runnable.getTaskName()+”的put()与“+elements.size()+”elements一起放置”);
要素。付诸表决(i);

啊,是的。好的僵局

发生的情况是:您向ExecutorService提交10个任务,然后通过
.put(i)
发送作业。当任务5的队列已满时,这将按预期阻止任务5。现在任务5当前没有执行,事实上也永远不会执行,因为任务0到4仍在阻塞您的FixedThreadPool,阻塞在
运行()方法中的
take()
等待来自
的新作业。put(i)
,它们永远不会得到新作业

这个错误是代码中的一个基本设计缺陷,有无数种方法可以修复它,其中之一就是增加线程池大小

我的建议是,您回到绘图板,重新思考主方法中的结构

既然你发布了你的代码,有一些提示:

1.: 发布您的整个代码可以解释为对“请修复我的代码”的调用,并鼓励您忽略所有不必要的细节(如所有的getter和setter)。也许检查一下

2.: 在同一个主体中发布两个类使事情变得有点复杂。下次再分吧

3.:(挑剔)
processs.get(roundRobinIndex++).getElements().put(i)
像您在这里所做的那样组合两个操作是一种糟糕的风格,因为它会使您的代码对其他人的可读性降低。你可以写下:
processs.get(i%noOfProcesses.getElements().put(i)

processes.get(roundRobinIndex++).getElements().put(i);
MyRunnable runnable = processes.get(roundRobinIndex++);
BlockingQueue<Integer> elements = runnable.getElements();
System.out.println("attempt to put() for " + runnable.getTaskName() + " with " + elements.size() + " elements");
elements.put(i);