如何在 Java-ee 中构建递归树?
How to build a recursive tree in Java-ee?
这是我的伪代码:
class Builder implements Callable<T> {
T obj;
ManagedExecutorService pool;
Builder (T obj, ManagedExecutorService pool){
this.obj = obj;
this.pool = pool;
}
T call(){
build();
}
private void build(){
// skip if already traversed
return isTraversed(obj);
// call db and get obj's one-to-many relationships
Collection<T> colOfChildObj = DbUtil.getChildrenPOJO(obj);
for (<Collection>T childObj : colOfChildObj){
this.pool.submit(new Builder(childObj, this.pool));
}
// set children as-is, when the submit above completes,
// it will update childObj and thus will reflect
// obj.childObj.itsChidren etc. For this though the caller
// has to wait until all submits are processed
obj.setChildren(colOfChildObj);
}
}
因为 Java-ee 不支持 ForkJoinPool - 这是不可能的。那么我该如何使用 ManagedThreadFactory and/or ManagedExecutorService 来实现呢?我真正的挑战是无法在 Java-ee 中调用 pool.shutdown() 或 pool.awaitTermination。所以,从来电者那里,如果我这样做:
class Caller () {
T getObjGraph(T rootObj){
pool.submit(new Builder(rootObj));
T objGraph = pool.get();
return objGraph;
}
}
然后我的方法不会等待所有 pool.submit(new Builder(childObj, pool)),因此我的对象没有设置所有内容并且不完整。我想将 pool.submit 返回的所有 Futures 放入阻塞队列中 - 但我不知道如何通知调用者我的树遍历已完成。当树遍历完成时,我确实有一个达到 0 的计数器,但是由于客户端正在提交顶级节点,我不确定如何让它在 Java-ee 中等待而没有 while(isCounter = 0 ) - 这是一只 CPU 猪。
有什么指点吗?
我想我明白你想做什么。您可以只使用 thread-safe 计数器,每次为给定节点创建和提交新任务时递增它,并在该节点的任务终止时递减它。
在主线程中,您等待一个锁,直到要处理的剩余节点数为 0。并且在每个任务中,您通知锁以发出一个 tack 已终止的信号。
这是一个完整的例子。它从一棵树开始,其中每个节点都有一个名称,然后将这棵树转换为另一棵树,其中每个节点 "Hello " 与原始名称连接。
public class Tree {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Node root = new Node("R");
Node c1 = new Node("C1");
Node c2 = new Node("C2");
root.addChild(c1);
root.addChild(c2);
Node gc11 = new Node("GC11");
Node gc12 = new Node("GC12");
c1.addChild(gc11);
c1.addChild(gc12);
Node gc21 = new Node("GC11");
Node gc22 = new Node("GC12");
c2.addChild(gc21);
c2.addChild(gc22);
System.out.println("root = " + root);
ExecutorService executor = Executors.newFixedThreadPool(4);
final Object lock = new Object();
final AtomicInteger remaining = new AtomicInteger(0);
Future<Node> result = executor.submit(new HelloTask(root, null, executor, remaining, lock));
synchronized (lock) {
while (remaining.get() != 0) {
lock.wait();
}
}
Node helloRoot = result.get();
System.out.println("helloRoot = " + helloRoot);
executor.shutdown();
}
private static class HelloTask implements Callable<Node> {
private final Node source;
private final Node parent;
private final ExecutorService executorService;
private final Object lock;
private final AtomicInteger remaining;
public HelloTask(Node source, Node parent, ExecutorService executorService, AtomicInteger remaining, Object lock) {
this.source = source;
this.parent = parent;
this.executorService = executorService;
this.lock = lock;
this.remaining = remaining;
remaining.incrementAndGet();
}
@Override
public Node call() throws Exception {
// simulate some time
Thread.sleep(1000L);
Node result = new Node("Hello " + source.getName());
if (parent != null) {
parent.addChild(result);
}
for (Node child : source.getChildren()) {
executorService.submit(new HelloTask(child, result, executorService, remaining, lock));
}
remaining.decrementAndGet();
synchronized (lock) {
lock.notifyAll();
}
return result;
}
}
private static class Node {
private final String name;
private final List<Node> children = new CopyOnWriteArrayList<>();
public Node(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Node> getChildren() {
return children;
}
public void addChild(Node child) {
this.children.add(child);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('\n');
children.forEach(sb::append);
return sb.toString();
}
}
}
这是我的伪代码:
class Builder implements Callable<T> {
T obj;
ManagedExecutorService pool;
Builder (T obj, ManagedExecutorService pool){
this.obj = obj;
this.pool = pool;
}
T call(){
build();
}
private void build(){
// skip if already traversed
return isTraversed(obj);
// call db and get obj's one-to-many relationships
Collection<T> colOfChildObj = DbUtil.getChildrenPOJO(obj);
for (<Collection>T childObj : colOfChildObj){
this.pool.submit(new Builder(childObj, this.pool));
}
// set children as-is, when the submit above completes,
// it will update childObj and thus will reflect
// obj.childObj.itsChidren etc. For this though the caller
// has to wait until all submits are processed
obj.setChildren(colOfChildObj);
}
}
因为 Java-ee 不支持 ForkJoinPool - 这是不可能的。那么我该如何使用 ManagedThreadFactory and/or ManagedExecutorService 来实现呢?我真正的挑战是无法在 Java-ee 中调用 pool.shutdown() 或 pool.awaitTermination。所以,从来电者那里,如果我这样做:
class Caller () {
T getObjGraph(T rootObj){
pool.submit(new Builder(rootObj));
T objGraph = pool.get();
return objGraph;
}
}
然后我的方法不会等待所有 pool.submit(new Builder(childObj, pool)),因此我的对象没有设置所有内容并且不完整。我想将 pool.submit 返回的所有 Futures 放入阻塞队列中 - 但我不知道如何通知调用者我的树遍历已完成。当树遍历完成时,我确实有一个达到 0 的计数器,但是由于客户端正在提交顶级节点,我不确定如何让它在 Java-ee 中等待而没有 while(isCounter = 0 ) - 这是一只 CPU 猪。
有什么指点吗?
我想我明白你想做什么。您可以只使用 thread-safe 计数器,每次为给定节点创建和提交新任务时递增它,并在该节点的任务终止时递减它。
在主线程中,您等待一个锁,直到要处理的剩余节点数为 0。并且在每个任务中,您通知锁以发出一个 tack 已终止的信号。
这是一个完整的例子。它从一棵树开始,其中每个节点都有一个名称,然后将这棵树转换为另一棵树,其中每个节点 "Hello " 与原始名称连接。
public class Tree {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Node root = new Node("R");
Node c1 = new Node("C1");
Node c2 = new Node("C2");
root.addChild(c1);
root.addChild(c2);
Node gc11 = new Node("GC11");
Node gc12 = new Node("GC12");
c1.addChild(gc11);
c1.addChild(gc12);
Node gc21 = new Node("GC11");
Node gc22 = new Node("GC12");
c2.addChild(gc21);
c2.addChild(gc22);
System.out.println("root = " + root);
ExecutorService executor = Executors.newFixedThreadPool(4);
final Object lock = new Object();
final AtomicInteger remaining = new AtomicInteger(0);
Future<Node> result = executor.submit(new HelloTask(root, null, executor, remaining, lock));
synchronized (lock) {
while (remaining.get() != 0) {
lock.wait();
}
}
Node helloRoot = result.get();
System.out.println("helloRoot = " + helloRoot);
executor.shutdown();
}
private static class HelloTask implements Callable<Node> {
private final Node source;
private final Node parent;
private final ExecutorService executorService;
private final Object lock;
private final AtomicInteger remaining;
public HelloTask(Node source, Node parent, ExecutorService executorService, AtomicInteger remaining, Object lock) {
this.source = source;
this.parent = parent;
this.executorService = executorService;
this.lock = lock;
this.remaining = remaining;
remaining.incrementAndGet();
}
@Override
public Node call() throws Exception {
// simulate some time
Thread.sleep(1000L);
Node result = new Node("Hello " + source.getName());
if (parent != null) {
parent.addChild(result);
}
for (Node child : source.getChildren()) {
executorService.submit(new HelloTask(child, result, executorService, remaining, lock));
}
remaining.decrementAndGet();
synchronized (lock) {
lock.notifyAll();
}
return result;
}
}
private static class Node {
private final String name;
private final List<Node> children = new CopyOnWriteArrayList<>();
public Node(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Node> getChildren() {
return children;
}
public void addChild(Node child) {
this.children.add(child);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append('\n');
children.forEach(sb::append);
return sb.toString();
}
}
}