有条件的任务调度 - 多线程Java应用程序
我有一个包含'资源管理器'类的多线程Java应用程序。有条件的任务调度 - 多线程Java应用程序
该类提供了可能作为初始化参数请求的资源列表。然后它检查每个文件的本地文件系统并将确定为本地的文件添加到列表中。
当类收到以下的资源请求之一发生:
如果资源被确定为本地(列表):提供URI,其中可以发现。
如果资源是远程的(不在列表中):安排工作人员获取资源。任务完成时,工作人员将通知经理,并且本地资源列表将被更新。 (请求线程不会等待 - 无论是否存在)。
由于多个线程可以请求资源,ReadWriteLock用于协调列表访问。许多线程可以同时读取列表,并且当需要更新时,将使用writeLock。
问题在于为任何特定的远程资源调度后台工作。如果多个线程安排资源相同资源的工作人员,则需要不必要的开销(即使重复任务因为检查此情况而未完全执行)。为了达到最高的效率可能我想了解一下下面的实现:
private final ReadWriteLock lock = new ReentrantReadWriteLock();
//assume each resource object has a volatile boolean "scheduled"
//with isScheduled() and setSheduled() setter/getter;
//assume the resource list is thread safe
public URI requestResource(Resource theResource){
URI resourceId = null;
//many threads can enter here
lock.readLock().lock();
try{
//if our resource is not in the list
if(!localResouces.contains(theResource)){
//double-check idiom...does it work here?
//if the resource isn't scheduled
if(!theResource.isScheduled()){
//only one thread may enter here
synchronized(this){
if(!theResource.isScheduled()){
//schedule task here...
theResource.setScheduled(true);
}
}
}
} else {
//its local, return the location
resouceId = theResource.getURI();
}
} finally {
lock.readLock().unlock();
}
//requesting object will deal with null value;
return resouceId;
}
和当工人完成:
public void update(Resource theResource){
//ensures no threads in the read block
lock.writeLock().lock();
try {
//update the list (would check result IRL, and not add if problem found)
localResources.add(theResource);
//set the scheduled field
theResource.setScheduled(false);
} finally {
lock.writeLock().unlock();
}
}
同样,我想最大限度地提高效率。我找不到与这种情况相匹配的示例 - 即允许对常用操作进行高吞吐量,同时允许以最小的阻塞/开销对任务进行调度。
这种方法有什么问题?第一种方法必须同时获得读取锁定和同步,但是update方法只需要获取写入锁定,因为isScheduled检查被封装在读取块中。这是否提供线程安全调度和数据访问?
编辑:
伊夫测试如上所述的方法和我看到正确的行为。我仍然不确定这是否真的是“线程安全的”。
我可能会做这种方式:
class Resource
Uri localUri;
volatile int state; // REMOTE, FETCHING, LOCAL
Resource()
if local
localUri = ...;
state = LOCAL;
else
state = REMOTE
URI requestResource()
if(state==LOCAL) // volatile read
return localUri;
if(state==FETCHING)
return null;
synchronized(lock)
if(state==LOCAL)
return localUri;
if(state==FETCHING)
return null;
// REMOTE, and it's my job to initiate fetching
state=FETCHING;
// do actaul fetching outside synchronized block
schedule fetching...
void onFetchingDone()
synchronized(lock)
if error
state=REMOTE; // to retry.
// if we consider error unrecoverable,
// we need an ERROR state.
else
...
loalUri = ...;
state = LOCAL; // volatile write
我不是这些操作效率方面的专家,但这似乎并不是最合适的解决方案。每个请求必须至少执行一次易失性读取。另外,如果一个资源'x'是远程的,那么每个请求它的线程都需要输入同步块(检查是否派发了一个工作者),直到由第一个请求生成的工作者返回。 – 2013-02-18 23:15:20
易失性读取比输入读取锁定便宜很多。在你的第二点上,我增加了一个在同步块之外的检查。代码可以进一步优化,我会在短时间内完成。 – irreputable 2013-02-18 23:22:49
你为什么需要本地资源列表?为什么不在'Resource'中有一个标志来表示它是本地的? – irreputable 2013-02-18 22:05:23
我的目标是尽可能减少同步块的入口。你能否详细说明这个建议如何帮助实现这一目标? – 2013-02-18 22:35:51