我可以在没有ExecutorService的情况下使用Callable线程吗?我们可以使用Runnable的实例和没有ExecutorService的Thread的子类,这个代码可以正常工作。但是这段代码始终如一:
public class Application2 {
public static class WordLengthCallable implements Callable {
public static int count = 0;
private final int numberOfThread = count++;
public Integer call() throws InterruptedException {
int sum = 0;
for (int i = 0; i < 100000; i++) {
sum += i;
}
System.out.println(numberOfThread);
return numberOfThread;
}
}
public static void main(String[] args) throws InterruptedException {
WordLengthCallable wordLengthCallable1 = new WordLengthCallable();
WordLengthCallable wordLengthCallable2 = new WordLengthCallable();
WordLengthCallable wordLengthCallable3 = new WordLengthCallable();
WordLengthCallable wordLengthCallable4 = new WordLengthCallable();
wordLengthCallable1.call();
wordLengthCallable2.call();
wordLengthCallable3.call();
wordLengthCallable4.call();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
}
使用ExecutorService,代码可以使用少量线程。我的错误在哪里?
答案 0 :(得分:30)
虽然interface
通常是根据预期的用例创建的,但它们绝不会被限制使用。
给定Runnable
您可以将其提交给ExecutorService
,或将其传递给Thread
的构造函数,或者您可以直接调用其run()
方法,就像您可以调用任何没有涉及多线程的interface
方法。还有更多的用例,例如AWT EventQueue.invokeLater(Runnable)
所以永远不要指望列表完整。
给定Callable
,您有相同的选项,因此请务必强调,与您的问题不同,直接调用call()
不涉及任何多线程。它只是像任何其他普通的方法调用一样执行方法。
由于没有构造函数Thread(Callable)
使用Callable
而Thread
没有ExecutorService
需要更多代码:
FutureTask<ResultType> futureTask = new FutureTask<>(callable);
Thread t=new Thread(futureTask);
t.start();
// …
ResultType result = futureTask.get(); // will wait for the async completion
答案 1 :(得分:6)
简单的直接答案是,如果要使用Callable创建和运行后台线程,则需要使用ExecutorService,当然,如果要获取Future对象或Futures集合,则需要使用ExecutorService。如果没有Future,您将无法轻松获取Callable返回的结果或轻松捕获生成的异常。当然你可以尝试将你的Callable包装在一个Runnable中,然后在一个Thread中运行它,但这会引起一个问题,因为这样做会导致你失去很多。
修改强>
你在评论中提问,
你的意思是下面的代码,哪个有效?
public class Application2 {
public static class WordLengthCallable implements Callable {
public static int count = 0;
private final int numberOfThread = count++;
public Integer call() throws InterruptedException {
int sum = 0;
for (int i = 0; i < 100000; i++) {
sum += i;
}
System.out.println(numberOfThread);
return numberOfThread;
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.exit(0);
}
public static class MyRunnable implements Runnable {
@Override
public void run() {
try {
new WordLengthCallable().call();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
我的回答:是的。链接中的代码&#34;排序&#34;作品。是的,它会创建后台线程,但会丢弃Callables中执行的计算结果,并忽略所有异常。这就是我的意思&#34;因为这样做会损失很多&#34;。
如,
ExecutorService execService = Executors.newFixedThreadPool(THREAD_COUNT);
List<Future<Integer>> futures = new ArrayList<>();
for (int i = 0; i < THREAD_COUNT; i++) {
futures.add(execService.submit(new WordLengthCallable()));
}
for (Future<Integer> future : futures) {
try {
System.out.println("Future result: " + future.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
Thread.sleep(1000);
System.out.println("done!");
execService.shutdown();
编辑2
或者,如果您希望在结果发生时返回结果,请使用CompletionService来包装您的ExecutorService,这是我以前从未尝试过的:
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletionServiceExample {
public static class WordLengthCallable implements Callable<Integer> {
private Random random = new Random();
public Integer call() throws InterruptedException {
int sleepTime = (2 + random.nextInt(16)) * 500;
Thread.sleep(sleepTime);
return sleepTime;
}
}
private static final int THREAD_COUNT = 4;
public static void main(String[] args) throws InterruptedException {
ExecutorService execService = Executors.newFixedThreadPool(THREAD_COUNT);
CompletionService<Integer> completionService = new ExecutorCompletionService<>(
execService);
for (int i = 0; i < THREAD_COUNT; i++) {
completionService.submit(new WordLengthCallable());
}
execService.shutdown();
try {
while (!execService.isTerminated()) {
int result = completionService.take().get().intValue();
System.out.println("Result is: " + result);
}
} catch (ExecutionException e) {
e.printStackTrace();
}
Thread.sleep(1000);
System.out.println("done!");
}
}
答案 2 :(得分:0)
是的,您可以直接使用自己的线程中的Callable的call()
方法或Runnable的run()
方法。但是,这应该是您在特殊情况下的最后手段(例如集成遗留代码或单元测试)。扫描程序可能会检测到此情况并提醒您可能存在架构问题,因此最好不要这样做。
您也可以使用自己的ExecutorService
(或使用Guava&#39; s MoreExecutors.sameThreadExecutor()
),它基本上调用调用线程中的调用。这将隔离你的'#cle;&#34;使用此Executor的接口,并允许它随时使用不同的Executor。
BTW:小心,当你从Thread
继承时,你不应该在没有开始/停止的情况下使用它,因为这可能会导致泄漏。这是错误扫描程序直接调用run()方法时发出警报的原因之一。
答案 3 :(得分:0)
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MainClass {
public static void main(String[] args) {
try {
Callable<String> c = () -> {
System.out.println(Thread.currentThread().getName());
return "true";
};
FutureTask<String> ft = new FutureTask<String>(c);
Thread t = new Thread(ft);
t.start();
String result = ft.get();
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
Output:
Thread-0
true
*/