我有一个包含“资源管理器”类的多线程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();
}
}
同样,我想最大限度地提高效率。我找不到匹配这种情况的示例 - 即允许常规操作的高吞吐量,同时允许以最小的阻塞/开销调度任务。
这种方法有什么问题?第一种方法必须同时获取读锁定和同步,但更新方法只需要获取写锁定,因为isScheduled的检查封装在读取块中。这是否提供了线程安全的调度和数据访问?
编辑:
我测试了上述方法,我看到了正确的行为。我仍然不确定这是否真的是“线程安全的”。
答案 0 :(得分:1)
我可能会这样做:
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