我是Java并发的新手,我对这里的几个概念和实现问题感到困惑。希望你们能帮忙。
说,我有一个存储在线程安全列表包装器中的任务列表:
ListWrapper jobs = ....
'ListWrapper'已经同步了fetch / push / append函数,这个'jobs'对象将由多个工作线程 shared 。
我有一个工人'Runnable'来执行任务:
public class Worker implements Runnable{
private ListWrapper jobs;
public Worker(ListWrapper l){
this.jobs=l;
}
public void run(){
while(! jobs.isEmpty()){
//fetch an item from jobs and do sth...
}
}
}
现在在main函数中我执行任务:
int NTHREADS =10;
ExecutorService service= Executors.newFixedThreadPool(NTHREADS);
//run threads..
int x=3;
for(int i=0; i<x; i++){
service.execute(new Worker(jobs) );
}
我用'x = 3'测试了这段代码,我发现只有3个线程同时运行;但是当我设置'x = 20'时,我发现只有10(= NTHREADS)同时运行。在我看来,实际线程的数量是两个值的 min 。
现在我的问题是:
1)我应该设置哪个值('x'或'NTHREADS')来控制并发线程的数量?或者我选择哪个都没关系?
2)这种方法与简单地使用Producer-Consumer模式有何不同 - 创建固定数量的'stud'线程来执行任务(如下面的代码所示)?
Thread t1= new Worker(jobs);
Thread t2= new Worker(jobs);
...
t1.join();
t2.join();
...
非常感谢!!
答案 0 :(得分:2)
[[这里有一些好的答案,但我想我会添加更多细节。 ]
我用'x = 3'测试了这段代码,我发现只有3个线程同时运行;但是当我设置'x = 20'时,我发现只有10(= NTHREADS)同时运行。在我看来,实际线程的数量是两个值的最小值。
不,不是真的。我怀疑你没有看到20个线程的原因是线程已经完成或尚未启动。如果你拨打new Thread(...).start()
20次,那么你将获得20个线程。但是,如果您立即检查它们中的任何一个可能实际上已经开始运行,或者如果您稍后检查它们可能已经完成。
1)我应该设置哪个值('x'或'NTHREADS')来控制并发线程的数量?或者我选择哪个都没关系?
引用Executors.newFixedThreadPool(...)
的Javadoc:
创建一个线程池,该线程池重用在共享无界队列中运行的固定数量的线程。在任何时候,最多
nThreads
个线程都将是活动处理任务。
因此,更改NTHREADS
常量会更改池中运行的线程数。更改x
会更改这些线程执行的作业数。您可以在池中拥有2个线程并提交1000个作业,或者您可以拥有1000个线程,并且只提交1个作业供他们使用。
顺便说一下,在提交完所有作业之后,你应该关闭池,如果所有作业都已运行,它会停止所有线程。
service.shutdown();
2)这种方法与简单地使用Producer-Consumer模式有何不同 - 创建固定数量的'stud'线程来执行任务(如下面的代码所示)?
它的不同之处在于它为你完成了所有繁重的工作。
ListWrapper
个工作,因为您在ExecutorService
内有一个工作。您只需将作业提交到ExecutorService
并跟踪它们,直到线程可用于运行它们。ExecutorService
为您启动/重新启动线程。submit(Callable)
方法并使用Future
获取作业结果。等等。自己做这些代码将变得更难以维护,需要维护更多代码,并且很可能不会像经过测试和优化的JDK中的代码一样好。
答案 1 :(得分:1)
使用线程池时,您不应该自己创建线程。您应该使用实现WorkerThread
但不是线程的类来代替Runnable
类。将Thread
对象传递给线程池不会使线程实际运行。该对象将被传递给另一个内部线程,该线程将只执行run
类的WorkerThread
方法。
ExecutorService
与您编写程序的方式完全不符。
在您现在拥有的代码中,当WorkerThread
为空时,这些ListWrapper
将停止运行。如果您随后在列表中添加内容,则不会发生任何事情。这绝对不是你想要的。
你应该摆脱ListWrapper
并简单地将你的任务直接放入线程池中。线程池已经包含了线程之间共享的内部作业列表。您应该只将作业提交到线程池,它将相应地处理它们。
回答你的问题:
1)我应该设置哪个值(&#39; x&#39;或者&#39; NTHREADS&#39;)来控制并发线程的数量?或者我选择它并不重要?
NTHREADS,线程池将创建必要数量的线程。
2)这种方法与简单地使用Producer-Consumer模式有什么不同 - 创建固定数量的&#39; stud&#39;执行任务的线程(如下面的代码所示)?
只是ExecutorService
为你自动化很多事情。您可以从许多不同的线程池实现中进行选择,您可以轻松地替换它们。您可以使用例如预定的执行程序。你获得额外的功能。为什么重新发明轮子?
答案 2 :(得分:0)
对于1)NTHREADS
是池将同时运行的最大线程,但这并不意味着总会有许多线程在运行。它只会使用最大值所需的数量......在您的情况下为3。
正如文档所说:
在任何时候,最多nThreads线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到线程可用
http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
至于2)使用Java的并发执行器框架是新代码的首选。你可以免费获得很多东西,并且无需自己处理所有繁琐的线程工作。
答案 3 :(得分:0)
The number of threads passed into newFixedThreadPool is at most how many threads could be running executing your tasks。如果您只提交了三个任务,我希望ExecutorService只创建三个线程。
回答你的问题:
您应该使用传递给构造函数的数字来控制将用于执行任务的线程数。
这不同,因为ExecutorService为您提供了额外的功能,以及它为您提供的灵活性,例如您需要更改ExecutorService类型或您将运行的任务数量(更少)要改变的代码行。
答案 4 :(得分:0)
所有正在发生的事情是执行程序服务只创建所需数量的线程。 NTHREADS
实际上是它将创建的最大线程数。
如果只有3个任务需要完成,那么预先创建10个线程是没有意义的,其他7个线程只会消耗资源。
如果您提交更多而不是NTHREADS
个任务,那么它将同时处理该号码,其余的将在队列中等待,直到线程空闲为止。
除了为您处理线程管理和调度之外,这与创建自己的线程的固定集没有任何不同。执行程序服务还会重新启动线程,如果它们被您的任务中的恶意异常所杀死,否则您必须编写代码。
请参阅:Executorservice.newFixedThreadPool上的Javadoc