这是出于学习目的。
想象一下我想计算素数并使用ThreadPoolExecutor
来进行计算。
在下面可以看到我当前的实现,这很愚蠢。
我的结构:
我会生成一定范围内的数字。
对于每个生成的数字,创建一个任务以检查给定数字是否为质数。
如果为质数,则运算结果为数字,否则为null
。
收集器遍历结果列表并检查是否存在数字或null
。如果是数字,则将该数字写到某个文件中(此处:按位数排序)
我想做的是:如果任务中要检查的数字不是素数,请从列表中删除我的未来/取消它。据我所知,只有执行者本身可以取消未来。 我要的是任务本身说“嘿,我知道我的结果对您没有用,所以在遍历列表时请忽略我”。 我不知道该怎么做。
我现在正在做什么(相关部分):
final List<Future<Long>> resultList = new ArrayList<>();
final BlockingQueue<Runnable> workingQueue = new ArrayBlockingQueue<>(CAPACITY);
final ExecutorService exec = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() - 2,
Runtime.getRuntime().availableProcessors() - 1,
5, TimeUnit.SECONDS,
workingQueue,
new ThreadPoolExecutor.CallerRunsPolicy()
);
for (long i = GENERATEFROM; i <= GENERATETO; i++) {
Future<Long> result = exec.submit(new Worker(i));
resultList.add(result);
}
Collector collector = new Collector(resultList,GENERATETO);
collector.start();
exec.shutdown();
一个工人在那里执行一个任务(是素数吗?)
public class Worker implements Callable<Long> {
private long number;
public Worker(long number) {
this.number = number;
}
//checks whether an int is prime or not.
boolean isPrime(long n) {
//check if n is a multiple of 2
if (n % 2 == 0) return false;
//if not, then just check the odds
for (long i = 3; i * i <= n; i += 2) {
if (n % i == 0)
return false;
}
return true;
}
@Override
public Long call() throws Exception {
if (isPrime(number)) {
return number;
}
return null;
}
}
为了完整起见,我的收藏家:
public class Collector {
private List<Future<Long>> primeNumbers;
private long maxNumberGenerated;
private HashMap<Integer, PrintWriter> digitMap;
private final long maxWaitTime;
private final TimeUnit timeUnit;
public Collector(List<Future<Long>> primeNumbers, long maxNumberGenerated) {
this.primeNumbers = primeNumbers;
this.maxNumberGenerated = maxNumberGenerated;
this.digitMap = new HashMap<>();
this.maxWaitTime = 1000;
this.timeUnit = TimeUnit.MILLISECONDS;
}
public void start() {
try {
//create Files
int filesToCreate = getDigits(maxNumberGenerated);
for (int i = 1; i <= filesToCreate; i++) {
File f = new File(System.getProperty("user.dir") + "/src/solutionWithExecutor/PrimeNumsWith_" + i +
"_Digits.txt");
PrintWriter pw = new PrintWriter(f, "UTF-8");
digitMap.put(i, pw);
}
for (Future<Long> future : primeNumbers) {
Object possibleNumber = future.get();
if (possibleNumber != null) {
long numberToTest = (long) possibleNumber;
int numOfDigits = getDigits(numberToTest);
PrintWriter correspondingFileWriter = digitMap.get(numOfDigits);
correspondingFileWriter.println(possibleNumber.toString());
correspondingFileWriter.flush();
}
}
for (PrintWriter fw : digitMap.values()) {
fw.close();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private int getDigits(long maxNumberGenerated) {
return String.valueOf(maxNumberGenerated).length();
}
}
答案 0 :(得分:1)
我想做的是:如果任务中要检查的数字不是素数,请从列表中删除我的未来/取消它。据我所知,只有执行者本身可以取消未来。
在我看来,这似乎是不必要的优化。 Future
在那里,任务可以返回一个值。一旦任务确定它不是素数并返回null
,与Future
关联的程序的“成本”就可以忽略不计。没有什么可以“取消”的。该任务已经完成,剩下的就是允许Future
传回null
或素数Long
的内存。
由于我们在谈论学习,因此在许多情况下,程序员会过于担心性能,因此我们经常花时间优化应用程序的某些部分,但这并不是问题所在。如果我看到使用某个JVM监视器(也许是jconsole),该应用程序内存不足,那么我可能会担心Futures
的列表,但否则我会编写干净且易于维护的代码。
如果您真的担心Future
,那么根本不要将它们保存在列表中,而只需在主要检查任务和主线程之间共享一个BlockingQueue<Long>
。主检查作业将add(...)
放入队列,而主线程将take()
。您应该考虑将null
放在列表中,因为除非您计算结果,否则您将不知道主要任务是否已完成。您需要检查X个随机数,然后知道从BlockingQueue<Long>
中获取X个结果(空值或数字)时就完成了。
希望这会有所帮助。