如何在Java中同时计算近似值?

时间:2016-06-06 09:20:47

标签: java multithreading java-8

这可能是一个有许多可能答案的问题,但我要求的是最好的设计,而不是"如何才能完成这些#34;

假设我们正在实现一个带有计算Pi的UI的程序。我可以打一个"开始"按钮开始计算和"停止"按钮中止计算,给我一个消息框,目前为止计算的Pi精度值最高。

我想直接的方法是在新线程中启动Runnable。 runnable计算Pi,并将当前值存储在共享变量中,两个线程都可以访问。 "停止"将中止线程,并显示共享变量。

我觉得这可以更优雅地实施,但我不确定如何。也许使用CompletableFuture?

我宁愿在没有向我的项目中添加任何新库的情况下解决这个问题,但是如果你知道一个特别支持这个库的库,请将它留在评论中。

显然,计算Pi永远不会完成。如果解决方案也支持例如,那将是很棒的。计算国际象棋中的最佳动作。如果有足够的时间,这将完成,但通常必须中止,返回到目前为止最好的举动。

2 个答案:

答案 0 :(得分:2)

参考你计算Pi或计算国际象棋中最佳动作的例子,你的近似算法本质上是迭代的。就像国际象棋的Pi和MCMC的随机抽样一样。这让我想到两个appraoches。

1。使用threadsafe标志

Cou可以使用AtomicBoolean这是一个线程安全的布尔变量。您需要将它传递给Runnable并在计算近似值时检查其状态。同时你停止计算的按钮监听器能够设置变量。

2。计算小块

算法的迭代特性使得分割计算成为可能,并在以后再次聚合。例如,您计算1000次迭代,您可以将其拆分为200次迭代的块,计算这5个块并聚合结果。

我现在建议使用ExecutorCompletionServiceTimerTask。我们的想法是计算少量迭代,这些迭代只需要很短的时间,并使用Executor以新的Runnable排斥TimerTask。让我们说计算5个runnables需要1秒钟,你的计时器任务会每5秒将5个Runnables放入Executor。当您点击停止按钮时,您将停止产生并等待待处理任务完成后收集结果并产生结果。

当然,在调用完成服务的关闭方法后,你还需要一个告诉TimerTask停止的变量,但是这个变量不是线程安全的。这种方法的另一个好处是你的计算是并发的,你可以很容易地充分利用任何CPU,只是产生更多的Runnables。同时执行此操作可以让您在更短的时间内计算更多,并获得更好的近似值。

答案 1 :(得分:0)

您的问题是如何实现仍然可以提供结果的可停止任务。近似值是一个很好的例子,但对于解决方案可以忽略。

例如,FutureTask不会起作用,因为这些合同是他们在完成后自行决定的,他们只能得到结果或被取消。

共享(例如volatile)变量听起来合理但有其缺点。当在紧密循环中定期更新时,您可能会观察到比使用局部变量更差的性能,并且只有在对象是例如对象时才能读取共享对象的状态。不可改变的,或者可以保证读写的顺序正确。

您还可以构建具有结果传递BlockingQueue的内容,其中一旦请求中断,计算线程就会将当前结果(甚至是结果的定期更新)放入其中。

但最好的解决方案可能是(共享)CompletableFuture。排序单个结果项队列,但它具有更好的语义来报告异常。

示例:

CompletableFuture<Integer> sharedFuture = new CompletableFuture<>();

Thread computing = new Thread(() -> {
    int value = 1;
    try {
        while (!Thread.currentThread().isInterrupted() &&
                !sharedFuture.isDone()) { // check could be omitted
            value = value * 32 + 7;
        }
        sharedFuture.complete(value);
    } catch (Throwable t) {
        sharedFuture.completeExceptionally(t);
    }
});
computing.start();

try {
    Thread.sleep((long) (5000 * Math.random()));
} catch (InterruptedException ignored) {
}

computing.interrupt();
System.out.println(sharedFuture.get());

http://ideone.com/8bpEGV

执行该任务的方式并不重要。您可以使用Thread而不是ExecutorService以上,而是取消Future而不是中断线程。