2013-01-03 185 views
2

我有一個集合,我想產生一些線程來做一些繁重的工作。集合中的每個元素都必須處理一次且只能處理一次。我想保持同步儘可能小,我想出了下面的代碼:這是多線程迭代安全嗎?

//getting the iterator is actually more complicated in my specific case 
final Iterator it = myCollection.terator(); 
Thread[] threads = new Thread[numThreads]; 

for(int i = 0; i < numThreads; i++) { 
    threads[i] = new Thread(new Runnable() { 
     public void run() { 
      Object obj = null; 
      while(true) { 
       synchronized (it) { 
        if(it.hasNext()) 
         obj = it.next(); 
        else 
         return; 
       } 
       //Do stuff with obj 
      } 
     } 
    }); 
    threads[i].start(); 
} 

for (Thread t : threads) 
    try { 
     t.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

注:沒有線程將永遠通過添加或刪除項目,而

「所有與obj做的東西」修改集合

這段代碼與我在周圍發現的人們傾向於同步集合本身,使用Collection.synchronizedStuff..,或者他們只是在整個迭代中同步的示例完全不同。在我的研究過程中,我還發現可能使用ThreadPoolExecutor更好的替代方案,但讓我們暫時忘掉它...

考慮到上面的註釋1,代碼是否安全?如果不是,爲什麼?

+1

只要注1仍然是真的,它會沒事的。執行者絕對是這樣的事情的途徑。 – BevynQ

+0

如果您爲了性能原因試圖保持「同步」塊不變,那麼您可能會驚訝在每個(本地)線程的創建和執行過程中花費了多少時間,而是按照建議使用執行程序。 –

+0

你的概念代碼看起來不錯。應該工作良好。訪問迭代器(包括檢查和讀取對象)是同步的,所以沒有問題。 – xagyg

回答

5

我根本不會使用同步。

我會有一個將任務添加到ExecutorService的循環。

ExecutorService es = Executors.newFixedThreadPool(nThreads); 

for(final MyType mt: myCollection) 
    es.submit(new Runnable() { 
     public void run() { 
      doStuffWith(mt); 
     } 
    }); 
es.shutdown(); 
es.awaitTermination(1, TimeUnit.HOURS); 

如果您刪除了創建和關閉線程池的需要,它甚至更短。

+0

我相信如果你的'mycollection'沒有改變,它就可以工作。 –

+0

這是一個聰明的主意,附加每個項目一個不同的線程... – JasonHuang

0

我認爲這將是更好地使MyCollection的決賽,將代碼修改

public void run() { 
     Object obj = null; 
     for (Object e : myCollection) { 
      obj = e; 
     } 

的for-each創建中的每個線程,因此不需要synchronziation一個新的迭代。

+0

hmmm ...使用此代碼,每個線程將執行與集合中的每個對象的東西,而我需要每個對象只能被操縱一次。此外,增強型for循環在內部使用迭代器(它應該調用hasNext()和next()),如果沒有同步,代碼可能會中斷。 – Gevorg

+0

爲每個線程創建一個迭代器,我不認爲這是一個好主意。這是浪費資源帶來很多開銷。 – JasonHuang