2013-02-18 49 views
1

我有一個包含'資源管理器'類的多線程Java應用程序。有條件的任務調度 - 多線程Java應用程序

該類提供了可能作爲初始化參數請求的資源列表。然後它檢查每個文件的本地文件系統並將確定爲本地的文件添加到列表中。

當類收到以下的資源請求之一發生:

  1. 如果資源被確定爲本地(列表):提供URI,其中可以發現。

  2. 如果資源是遠程的(不在列表中):安排工作人員獲取資源。任務完成時,工作人員將通知經理,並且本地資源列表將被更新。 (請求線程不會等待 - 無論是否存在)。

由於多個線程可以請求資源,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檢查被封裝在讀取塊中。這是否提供線程安全調度和數據訪問?

編輯:

伊夫測試如上所述的方法和我看到正確的行爲。我仍然不確定這是否真的是「線程安全的」。

+0

你爲什麼需要本地資源列表?爲什麼不在'Resource'中有一個標誌來表示它是本地的? – irreputable 2013-02-18 22:05:23

+0

我的目標是儘可能減少同步塊的入口。你能否詳細說明這個建議如何幫助實現這一目標? – 2013-02-18 22:35:51

回答

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 
+0

我不是這些操作效率方面的專家,但這似乎並不是最合適的解決方案。每個請求必須至少執行一次易失性讀取。另外,如果一個資源'x'是遠程的,那麼每個請求它的線程都需要輸入同步塊(檢查是否派發了一個工作者),直到由第一個請求生成的工作者返回。 – 2013-02-18 23:15:20

+0

易失性讀取比輸入讀取鎖定便宜很多。在你的第二點上,我增加了一個在同步塊之外的檢查。代碼可以進一步優化,我會在短時間內完成。 – irreputable 2013-02-18 23:22:49