我正在编写多线程应用程序,它使用Phaser知道何时完成工作。问题是,在ExecutorCompletionService中,队列中甚至可以有100k的线程,但是Phaser中的最大派对数量是65535.当到达65536方时,我该怎么办?
我的示例代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws Exception {
ExecutorService ec = Executors.newFixedThreadPool(10);
ExecutorCompletionService<List<String>> ecs = new ExecutorCompletionService<List<String>>(
ec);
Phaser phaser = new Phaser();
// register first node/thread
ecs.submit(new SimpleParser("startfile.txt"));
phaser.register();
Future<List<String>> future;
do {
future = ecs.poll();
if(future!=null && future.get() != null) {
addParties(phaser, future.get(), ecs);
phaser.arriveAndDeregister();
}
if (phaser.isTerminated()) {
ec.shutdown();
}
} while (!ec.isShutdown() && !phaser.isTerminated());
}
public static void addParties(Phaser p, List<String> filenames,
ExecutorCompletionService<List<String>> ecs) {
for (int i = 0; i < filenames.size(); i++) {
ecs.submit(new SimpleParser(filenames.get(i)));
//PROBLEM = What to do when Phaser has 65535+ unarrived parties
p.register();
}
}
static class SimpleParser implements Callable<List<String>> {
String fileName;
public SimpleParser(String fileName) {
this.fileName = fileName;
}
@Override
public List<String> call() throws Exception {
return parseFile();
}
private List<String> parseFile() {
return new ArrayList<String>(Arrays.asList(new String[] {
"somefilename1.txt", "somefilename2.txt" }));
}
}
}
问题出在addParties()方法中。单线程(SimpleParser)可以返回100个新文件名,并且将有100个新线程提交给ExecutorCompletionService,并且100个新方在Phaser中注册。 我试过用这样的东西:
if(p.getUnarrivedParties() == 65535)
p = new Phaser(p);
并创建一个阶段链,但它没有帮助,因为p.getUnarrivedParties()返回0,但我无法注册它的下一个派对...
System.out.println(p.getUnarrivedParties());
if(p.getUnarrivedParties() == 65535) {
p = new Phaser(p);
System.out.println(p.getUnarrivedParties());
}
p.register();
打印:
65535
0
并抛出IllegalStateException
那么如何创建与旧版Phaer相关的新Phaser呢?
//修改
谢谢@bowmore。 我还有两个问题。
让我们看看这个例子:
import java.util.concurrent.Phaser;
public class Test2 {
public static void main(String[] args) {
Phaser parent = new Phaser();
Phaser child1 = new Phaser(parent);
Phaser child2 = new Phaser(parent);
child1.register();
child2.register();
System.out.println("Parent: "+parent.isTerminated());
System.out.println("Child1: "+child1.isTerminated());
System.out.println("Child2: "+child1.isTerminated()+"\n");
child1.arriveAndDeregister();
System.out.println("Parent: "+parent.isTerminated());
System.out.println("Child1: "+child1.isTerminated());
System.out.println("Child2: "+child2.isTerminated()+"\n");
child2.arriveAndDeregister();
System.out.println("Parent: "+parent.isTerminated());
System.out.println("Child1: "+child1.isTerminated());
System.out.println("Child2: "+child2.isTerminated()+"\n");
}
}
打印:
Parent: false
Child1: false
Child2: false
Parent: false
Child1: false
Child2: false
Parent: true
Child1: true
Child2: true
为什么在child1.arriveAndDeregister()之后; child1没有被终止,如何检查它是否确实存在?
第二个问题。 我问到在达到65535个派对之后创建新的Phaser,因为我认为创建数千个新对象是没用的 - 你认为不存在内存问题,或者它甚至可能会影响性能吗?
答案 0 :(得分:3)
不是使用现有的Phaser
新进程注册,而是可以在新创建的原始子Phaser
上注册。只需将父Phaser
提供给孩子的构造函数即可创建子Phaser
。
public static void addParties(Phaser p, List<String> filenames,
ExecutorCompletionService<List<String>> ecs) {
Phaser newPhaser = new Phaser(p);
for (int i = 0; i < filenames.size(); i++) {
ecs.submit(new SimpleParser(filenames.get(i)));
newPhaser.register();
}
}
如果您只想在达到某个阈值时创建子Phasers,您可以检查注册方的数量,而不是未获得的数量:
public static void addParties(Phaser p, List<String> filenames, ExecutorCompletionService<List<String>> ecs) {
Phaser toRegister = p.getRegisteredParties() > THRESHOLD ? new Phaser(p) : p;
for (int i = 0; i < filenames.size(); i++) {
ecs.submit(new SimpleParser(filenames.get(i)));
//PROBLEM = What to do when Phaser has 65535+ unarrived parties
toRegister.register();
}
System.out.println(p.getRegisteredParties());
}
编辑:
要跟进问题1:孩子Phaser
与根Phaser
分享他们的终止状态,这里是isTerminated()
的实施
public boolean isTerminated() {
return root.state < 0L;
}
跟进问题2:父母Phasers实际上并没有提及他们的孩子Phasers。一旦不再引用子移相器,它就有资格进行垃圾收集。你最好遵循javadoc中的建议:
TASKS_PER_PHASER的最佳值主要取决于预期的同步速率。低至4的值可能适用于极小的每阶段任务组(因此高速率),或者对于极大的任务组则高达数百个。
分层的主要原因是减少了大量的同步争用,因此如果你有轻量级的任务,每个移相器的任务就更少了。配置不同的设置以调整这些内容永远不会受到伤害。