条件任务调度 - 多线程Java应用程序

时间:2013-02-18 21:55:29

标签: java multithreading concurrency

我有一个包含“资源管理器”类的多线程Java应用程序。

该类提供了可作为初始化参数请求的资源列表。然后,它检查本地文件系统中的每个文件,并将确定为本地的文件系统添加到列表中。

当类收到资源请求时,会发生以下情况之一:

  1. 如果确定资源是本地的(在列表中):提供可以找到它的URI。

  2. 如果资源是远程的(不在列表中):安排工作人员获取资源。工作人员将在任务完成时通知经理,并更新本地资源列表。 (请求线程不等待 - 无论是否存在)。

  3. 由于多个线程可以请求资源,因此使用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的检查封装在读取块中。这是否提供了线程安全的调度和数据访问?

    编辑:

    我测试了上述方法,我看到了正确的行为。我仍然不确定这是否真的是“线程安全的”。

1 个答案:

答案 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