将JAVA Servlet中的Runnable.run()转换为Callable.call()

4

我有一个问题,就是在下面的代码中将我的可运行接口转换为可调用接口。我需要改变,因为我需要通过线程返回Sting[][] isRs。

当我只是将接口更改为callable并将.run()更改为.call()时,new Thread(new Worker(startSignal, doneSignal, i)).start();将无法工作。

CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(3); // 3 tasks

class Worker implements Runnable {
    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    private final int threadNumber;

    // you can pass additional arguments as well
    Worker(CountDownLatch startSignal, CountDownLatch doneSignal, int threadNumber) {
        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.threadNumber = threadNumber;
    }

    public void run() {
        try {
            startSignal.await();

            if (threadNumber == 1) {
                String[][] isRs = getIS(erg1, erg2, request);
            }

            if (threadNumber == 2) {
                getIW(erg1, erg2, request);
            }

            if (threadNumber == 3) {
                getIN(search_plz, request);
            }

            doneSignal.countDown();
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }
}

// 3 new threads are started
for (int i = 1; i <= 3; i++) {
    new Thread(new Worker(startSignal, doneSignal, i)).start();
}

startSignal.countDown(); // let all threads proceed
try {
    doneSignal.await(); // wait for all to finish
    // all 3 tasks are finished and do whatever you want to do next
} catch (Exception e) {

}

1
不要使用原始线程,而是使用“ExecutorService”。或者,更好的选择是使用Java 8的“CompleteableFuture”。 - Boris the Spider
如何做到这一点?这是否解决了我的问题? - user3876178
这个教程开始学习,然后继续深入。我想指出的是,您可以使用多态性解决if threadNumber == x的问题,从而得到更清晰的代码。 - Boris the Spider
你为什么想把 Runnable 改成 Callable - Braj
2个回答

4

你不能将 Callable 对象传递给 Thread 来执行。

使用 ExecutorService 来执行 Callable 对象。

你可以使用它的 submit() 方法来运行 Callable 对象:

<T> Future<T> submit(Callable<T> task)

你的类应该像这样:

class Worker {

    private final CountDownLatch startSignal;
    private final CountDownLatch doneSignal;
    private final int threadNumber;

    Worker(
        CountDownLatch startSignal,
        CountDownLatch doneSignal,
        int threadNumber
    ){

        this.startSignal = startSignal;
        this.doneSignal = doneSignal;
        this.threadNumber = threadNumber;

    }

    public String[][] getSomeStrArrArr() {

        try {

            startSignal.await();

            if (threadNumber == 1) {
                System.out.println("Running thread number 1");
            }

            if (threadNumber == 2) {
                System.out.println("Running thread number 2");
            }

            if (threadNumber == 3) {
                System.out.println("Running thread number 3");
            }

            doneSignal.countDown();

        } catch (InterruptedException ex) {

            System.out.println(
                    "Thread number "+threadNumber+" has been interrupted."
            );

        }

        // replace these 2 lines with the actual code to get the String[][]
        String[][] someStrArrArr = new String[1][1];
        someStrArrArr[0][0] = "Done with thread number "+threadNumber;

        return someStrArrArr;

    }

    public Callable<String[][]> getSomeCallableStrArrArr(){
        return new Callable<String[][]>() {
            public String[][] call() throws Exception {
                return getSomeStrArrArr();
            }
        };
    }

}

你可以这样开始:

    ExecutorService pool = Executors.newFixedThreadPool(3);
    Set<Future<String[][]>> set = new HashSet<Future<String[][]>>();
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(3);
    for (int i=1;i<=3;i++) {
        Worker worker = new Worker(startSignal,doneSignal,i);
        Callable<String[][]> callable =
                worker.getSomeCallableStrArrArr();
        Future<String[][]> future = pool.submit(callable);
        set.add(future);
    }

并且,获取并打印结果字符串:

    for(Future<String[][]> future : set){
        String[][] result = future.get();
        for (String[] strArr: result){
            for (String str: strArr){
                System.out.println(str);
            }
        }
    }

但是这个设计可以改进。请查看以下关于Callable的文档,了解它与Runnable的区别以及如何从这些差异中受益并正确地实现它:

接口 Callable

https://blogs.oracle.com/CoreJavaTechTips/entry/get_netbeans_6?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+corejavatechtips+(Core+Java+Technologies+Tech+Tips)

请查看此链接,我已经基于您的代码编写了一个示例,您可以运行和修改: http://ideone.com/blUQm0


这段代码非常错误。你的“Worker”无法编译。 - Boris the Spider
谢谢您的回复,但如果我理解您的例子正确,那么它对我没有什么帮助。我使用不同的线程的原因是,所有三个任务应该同时进行处理,因为它们需要一些时间。在您的例子中,它们将被同步处理,因为它们等待返回,或者我错了吗? - user3876178
好的,你是完全正确的。它是并行的。那么现在我在set中有了3个线程的3个结果? 我怎样才能将这3个String[][]结果从set中取出? - user3876178
你可以设置 call() 方法返回 String[][] 而不是 Void。 - NotGaeL
@elcodedocle 这仍然是错误的。而且这个例子也是错误的。它使用了原始类型,这是极其不好的实践。 - Boris the Spider
显示剩余15条评论

1
一旦您的类实现了callable接口,就会有一个名为call的方法和它的返回类型。您可以使用以下代码来执行ExecutorService:
ExecutorService service =  Executors.newSingleThreadExecutor();
Worker worker= new Worker (tartSignal, doneSignal,threadNumber);
Future<Integer> future = service.submit(worker);
Object result = future.get();

希望这能帮助解决您的问题。

Object result是call()的返回值吗?我该如何从result中获取返回的String[][]?另一个问题是,我的start和stopSignal不再起作用了。谢谢。 - user3876178
在您的工作类中实现Callable接口后,您的方法签名将从public void run()更改为public String[][] Call(),并且您的方法将返回String[][]。您将在String[][] result = future.get()中获取此值。 - prashant thakre
如果你想要生成3个线程,只需将Executors.newSingleThreadExecutor();更改为Executors.newFixedThreadPool(3);然后将其余的三行代码放入一个for循环中执行三次,其中threadNumber会相应地改变,最后确保将Call()方法的输出存储在ArrayList或HashMap中。如果不清楚,请更新说明。 - prashant thakre
我有一个一般性的问题:我需要在三个线程中并行处理三个任务,因为它们需要一些时间,这就是我使用线程的原因。现在它们不是同步处理吗?因为它们必须等待结果。 - user3876178
你是对的,startSignal.await 是无用的。 好的,问题1已经解决:Object result = future.get(); 必须在并行处理的代码之外。但我只得到了最后一个线程的结果。 - user3876178
显示剩余3条评论

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