【发布时间】:2018-11-30 03:33:36
【问题描述】:
这是为了学习目的。
想象一下,我想计算素数并使用ThreadPoolExecutor 来做。
下面你可以看到我当前的实现,这有点傻。
我的结构:
我生成一定范围内的数字。
对于每个生成的数字,创建一个任务来检查给定的数字是否是素数。
如果是素数,则运算结果为数,否则为null。
收集器遍历结果列表并检查是否有数字或null。如果是数字,则将该数字写入某个文件(此处:按位数排序)
我想做的是:如果要在任务中检查的数字不是素数,请从列表中删除我的未来/取消它。据我所知,只有 Executor 本身可以取消 Future。 我想要的是任务本身说“嘿,我知道我的结果对你没有用,所以请在遍历列表时忽略我”。 我不知道该怎么做。
我现在在做什么(相关部分):
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();
}
}
【问题讨论】:
-
不要使用
List<Future<Long>>,而是使用Map<Integer, Future<Long>>。当您从提交的任务中获得Future<Long>时,将其添加到带有i创建的地图中。然后,当任务注意到它的i不是素数时,将其从Map中删除。不过,Questin 是:为什么您甚至需要删除这些Futures?听起来像是您可以通过list.stream().filter(f -> f.get() != null).collect(Collectors.asList());处理的任务 -
仅供参考:
future.get()返回一个 Long,因此您无需转换为possibleNumber。您正在执行possibleNumber.toString()两次,一次在getDigits(...)内,一次直接。注意不是FileWriter的变量,如correspondingFileWriter或fw。此外,我会按需创建您的PrintWriters,以便从地图中获取它们,如果它们为空,请将PrintWriter和put(...)创建到地图中。您上面的循环可能会使PrintWriters 未关闭,因为它们不在digitalMap中。 -
是的,谢谢你的投入。这是我的第一个概念,还没有重构——你说的大部分已经完成了。我唯一没有做的是按需创建 PrintWriter,我认为最好事先进行创建,以提高可读性和在实际操作过程中更快
-
@Turing85 我查看了您的个人资料,您似乎对低级执行了解很多。使用 != null 而不是询问数字是否为空,流是否更快?我认为在检查是否存在实际地址(或该地址处的值,不知道 null 是如何工作的)或者它可能更慢的意义上是相同的,因为它必须等到所有期货都完成
-
@InDaPond 让我们在chat 讨论这个问题。
标签: java multithreading concurrency threadpool threadpoolexecutor