我有一个计算地图 http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/MapMaker.html#makeComputingMap%28com.google.common.base.Function%29 (with 软价值观 http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/MapMaker.html#softValues%28%29)我用它来缓存昂贵的计算结果。
现在我遇到一种情况,我知道某个特定的键可能会在接下来的几秒钟内被查找。该密钥的计算成本也比大多数密钥更高。
我想在最小优先级线程中提前计算该值,以便最终请求该值时它已经被缓存,从而提高响应时间。
执行此操作的好方法是什么:
- 我可以控制执行计算的线程(特别是其优先级)。
- 避免重复工作,即计算只进行一次。如果计算任务已经在运行,则调用线程将等待该任务,而不是再次计算该值(
FutureTask
实现这个。对于 Guava 的计算地图,如果您只调用,这是正确的get
但如果你把它与调用混合起来就不会了put
.)
- “提前计算值”方法是异步且幂等的。如果计算已经在进行中,它应该立即返回,而无需等待该计算完成。
- 避免优先级倒置,例如如果高优先级线程请求该值,而中优先级线程正在执行不相关的操作,但计算任务在低优先级线程上排队,则高优先级线程不得处于饥饿状态。也许这可以通过暂时提高计算线程的优先级和/或在调用线程上运行计算来实现。
如何在所有涉及的线程之间进行协调?
附加信息
我的应用程序中的计算是图像过滤操作,这意味着它们都是 CPU 密集型的。这些操作包括仿射变换(范围从 50μs 到 1ms)和卷积(最多 10ms)。当然,不同线程优先级的有效性取决于操作系统抢占较大任务的能力。
您可以通过使用带有 ComputedMap 的 Future 来安排后台计算的“仅一次”执行。 Future 代表计算值的任务。未来由 ComputedMap 创建,同时传递给执行服务 http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/util/concurrent/ExecutorService.html用于后台执行。执行器可以配置自己的线程工厂 http://download.oracle.com/docs/cd/E17409_01/javase/6/docs/api/java/util/concurrent/ThreadFactory.html创建低优先级线程的实现,例如
class LowPriorityThreadFactory implements ThreadFactory
{
public Thread newThread(Runnable r) {
Tread t = new Thread(r);
t.setPriority(MIN_PRIORITY);
return t;
}
}
当需要该值时,高优先级线程会从映射中获取 future,并调用 get() 方法来检索结果,并在必要时等待计算结果。避免优先级反转 http://en.wikipedia.org/wiki/Priority_inversion您向任务添加一些额外的代码:
class HandlePriorityInversionTask extends FutureTask<ResultType>
{
Integer priority; // non null if set
Integer originalPriority;
Thread thread;
public ResultType get() {
if (!isDone())
setPriority(Thread.currentThread().getPriority());
return super.get();
}
public void run() {
synchronized (this) {
thread = Thread.currentThread();
originalPriority = thread.getPriority();
if (priority!=null) setPriority(priority);
}
super.run();
}
protected synchronized void done() {
if (originalPriority!=null) setPriority(originalPriority);
thread = null;
}
void synchronized setPriority(int priority) {
this.priority = Integer.valueOf(priority);
if (thread!=null)
thread.setPriority(priority);
}
}
这负责将任务的优先级提高到调用线程的优先级get()
如果任务尚未完成,则在任务完成时将优先级返回到原来的优先级,无论正常还是其他。 (为了简单起见,代码不会检查优先级是否确实更高,但这很容易添加。)
当高优先级任务调用 get() 时,future 可能还没有开始执行。您可能会想通过对执行程序服务使用的线程数设置较大的上限来避免这种情况,但这可能是一个坏主意,因为每个线程都可以以高优先级运行,从而消耗与之前一样多的 cpu操作系统将其关闭。池的大小可能应该与硬件线程的数量相同,例如池的大小为Runtime.availableProcessors()
。如果任务尚未开始执行,而不是等待执行程序安排它(这是优先级反转的一种形式,因为高优先级线程正在等待低优先级线程完成),那么您可以选择从当前执行器并在仅运行高优先级线程的执行器上重新提交。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)