从Runnable返回一个值

70
Runnablerun 方法返回类型为 void,不能返回值。然而,我想知道是否有任何解决方法。 我有一个像这样的方法:
public class Endpoint {
    public method() {
       Runnable runcls = new RunnableClass();
       runcls.run()
    }
}

这个方法 run 是这样的:

public class RunnableClass implements Runnable {
    
    public JaxbResponse response;

    public void run() {
        int id = inputProxy.input(chain);
        response = outputProxy.input();
    }
}

我想在method中访问response变量,这是可能的吗?


1
哪里来的 response?或者更好的是,outputProxy - Less
这些方法在不同的类中,我应该编辑我的问题,抱歉让你感到困惑。 - Grzzzzzzzzzzzzz
8个回答

90

使用 Callable<V> 替代使用 Runnable 接口。

示例:

public static void main(String args[]) throws Exception {
    ExecutorService pool = Executors.newFixedThreadPool(3);
    Set<Future<Integer>> set = new HashSet<>();

    for (String word : args) {
      Callable<Integer> callable = new WordLengthCallable(word);
      Future<Integer> future = pool.submit(callable);
      set.add(future);
    }

    int sum = 0;
    for (Future<Integer> future : set) {
      sum += future.get();
    }

    System.out.printf("The sum of lengths is %s%n", sum);
    System.exit(sum);
}

在这个例子中,您还需要实现类WordLengthCallable,它实现了Callable接口。


"主线程中的异常,java.lang.Error: 未解决的编译问题:WordLengthCallable无法解析为类型。" 我不够聪明去修复它,但是它对我不起作用。 - localhost
我认为new HashSet<Future,Integer>>()应该是new HashSet<Future<Integer>>(). - localhost
2
@localhost 只是 Callable 接口的一个具体实现。更多关于 Callable 的信息请参考:https://blogs.oracle.com/CoreJavaTechTips/entry/get_netbeans_6 - Narendra Pathai
如果您的委托没有抛出任何异常,最好使用Supplier<T> - RubberDuck
谢谢,Narendra Pathai。然而,我有一个问题。.join正在阻塞我的UI,抵消了分离线程的所有好处。我该如何避免这种情况? - Felipe La Rotta

18
public void check() {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<Integer> result = executor.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            return 10;
        }
    });

    try {
        int returnValue = result.get();
    } catch (Exception exception) {
       //handle exception
    }
}

8
请看 Callable 类,它通常是通过 executor service 提交的。它可以返回一个未来对象,该对象在线程完成时返回。

7

是的,有解决办法。只需使用队列并将您想要返回的值放入其中。然后从另一个线程中获取此值。

public class RunnableClass implements Runnable{

        private final BlockingQueue<jaxbResponse> queue;


        public RunnableClass(BlockingQueue<jaxbResponse> queue) {
            this.queue = queue;
        }

        public void run() {
            int id;
            id =inputProxy.input(chain);
            queue.put(outputProxy.input());
        }
    }


    public class Endpoint{
        public method_(){
            BlockingQueue<jaxbResponse> queue = new LinkedBlockingQueue<>();

            RunnableClass runcls = new RunnableClass(queue);
            runcls.run()

            jaxbResponse response = queue.take(); // waits until takes value from queue
        }
    }

非常好,让我摆脱了一个只接受Runnable的方法的问题。谢谢。 - Greg Harley

3
如果您在 RunnableClass 中添加一个字段,您可以在 run 中设置它,并在 method_ 中读取它。然而, Runnable 是一个不好的(Java关键字) interface ,因为它对接口(概念)没有任何说明(API文档中唯一有用的一句话是:“方法run的一般合同是它可能采取任何行动。”)。更好的做法是使用一个更有意义的接口(可能会返回某些内容)。

你能提供一个有意义的接口的例子吗? - Bugs Happen
查看 OnClickHandler。 - Joel Teply

3

一种方法是,我们必须使用Future - Callable方法。

另一种方法是,不要返回值,而是将其保存在对象中。

示例:

class MainThread {
    public void startMyThread() {
        Object requiredObject = new Object(); //Map/List/OwnClass
        Thread myThread = new Thread(new RunnableObject(requiredObject)).start();
        myThread.join();

        System.out.println(requiredObject.getRequiredValue());    
    }
}



class RunnableObject implements Runnable {
    private Object requiredObject;

    public RunnableObject(Object requiredObject) {
        this.requiredObject = requiredObject;
    }

    public void run() {
        requiredObject.setRequiredValue(xxxxx);
    }
}

因为对象作用域在相同的作用域内,所以您可以将对象传递给线程并在主作用域中检索。但是,最重要的是,我们必须使用join()方法。因为主作用域应该等待线程完成其任务。

对于多个线程的情况,您可以使用List/Map来保存来自线程的值。


1
requiredObject.setRequiredValue() 不是 Object 中的一个方法。需要采用更复杂的方法来完成你想要做的事情:requiredObject = result;Object getResult() { return requiredObject; }。同时,开启新线程与原问题无关,也不是答案所必需的。顺便说一下,像 Narendra Pathai 所回答的那样使用 Callable 绝对是更好的方式。 - Frederic Leitenberger
这里,Object是已经在注释中提到的Map/List/OwnClass。我还清楚地提到了使用成员名称。需要的对象/值。如果它是自定义类,我们可以编写setter和getter方法。 - ramakrishna

1
尝试以下内容。
public abstract class ReturnRunnable<T> implements Runnable {

    public abstract T runForResult();

    @Override
    public void run() {
        runForResult();
    }
}

0

看一下可调用接口,也许这符合您的需求。您还可以尝试通过在run()方法内调用setter方法来获取响应字段的值。

public void run() {
    int id;
    id =inputProxy.input(chain);
    response = outputProxy.input();
    OuterClass.setResponseData(response);

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接