2009-10-19 20 views
8

此問題涉及Java集合 - 特別是Hashtable和Vector - 但也可能適用於其他地方。對接口和同步集合進行編程

我讀過很多地方編程接口有多好,我同意100%。例如,在不考慮底層實現的情況下編程到List接口的能力對解耦和測試目的無疑是有幫助的。通過集合,我可以看到ArrayList和LinkedList如何在不同的情況下適用,考慮到內部存儲結構,隨機訪問時間等方面的差異。然而,這兩個實現可以在相同的接口下使用......是很棒的。

我似乎無法放置的是某些同步實現(特別是Hashtable和Vector)如何適合這些接口。對我來說,他們似乎不適合這個模型。大多數底層數據結構的實現似乎在數據存儲方式(LinkedList,Array,排序樹等)方面有所不同,而同步則處理可以訪問數據的條件(鎖定條件)。讓我們看一個方法返回一個Map集合的例子:

public Map<String, String> getSomeData(); 

讓我們假設應用程序根本不關心併發性。在這種情況下,我們在任何通過接口返回的實現上進行操作......每個人都很高興。世界是穩定的。

但是,如果應用程序現在需要關注併發前端呢?我們現在無法在不考慮底層實現的情況下運行 - 散列表將會很好,但其他實現必須滿足。我們來考慮3種情況:

1)使用同步塊等在使用集合添加/刪除時強制進行同步。但是,如果同步實現(Hashtable)被返回,那麼這不會是過度的嗎?

2)更改方法簽名以返回Hashtable。然而,這將我們緊緊綁定到Hashtable實現,因此,編程接口的優勢被拋出窗口。

3)使用併發包並更改方法簽名以返回ConcurrentMap接口的實現。對我而言,這似乎是前進的方向。

從本質上講,似乎某些同步實現在集合框架內有點不適合,因爲在編程到接口時,同步問題幾乎迫使人們想到底層實現。

我完全忽略了這一點嗎?

謝謝。

+2

我認爲關鍵是在這裏或那裏添加一些「同步」關鍵字並不會使您的程序線程安全。即使使用java.util.concurrent集合,也希望將它們保留爲私有實現細節。 – 2009-10-19 21:50:50

回答

6

1)是的,這將是矯枉過正
2)正確的,不應該做
3)視情況而定。

的事情是,因爲你已經知道,編程接口描述應用程序做什麼(它不是怎麼做的,這是實現)

同步從隨後的實現(記得刪除,Vector和Hastable是前到Java 1.2後來的ArrayList和不同步HasMap,但他們都沒有實現List分別Map接口),因爲它們會導致性能損失,由於過大的同步。例如,如果您在單個線程中使用矢量,那麼您仍然在該單線程中獲得同步。

在多個線程之間共享數據結構是當設計應用程序時必須考慮的事情。在那裏,您將選擇您將使用的方法,並選擇負責保持數據狀態清潔的人員。

這裏就是你,你提到的選項1或3之間進行選擇。會有手動同步嗎?我們應該使用同步接口嗎?什麼版本我們會支持等等等等

舉例來說,如果你選擇1,你也可以在你的設計中拒絕某些實現(即向量)

數據同步是不是通過「運氣」真的發生了,你必須設計它才能正確發生,並且不會導致解決問題的更多問題。

在這個設計中,你應該注意的股權(實現)和/或您要使用的底層基礎架構。

,以避免過多的同步的最簡單方法是使用一成不變的數據,並不會與其他線程共享數據。

東西由Martin Fowler非常類似於分佈式計算的第一定律:

「因此,我們到我的分佈式對象設計的第一定律:請不要發佈你的對象。」

會的多線程應用程序的第一定律是:

的多線程應用程序第一定律:不共享您的數據?

:)

最後提醒:Collections類提供了一些接口的 「同步」 的版本:

Synchronized List
Synchronized Map
Synchronized Set

0

Java的VectorHashtable早說是在JDK 5加入。當時Vector寫電流併發包,人們認爲這是一個好主意,使之同步,則他們可能打在企業使用的性能瓶頸。併發性當然是代碼到接口模塊化可能並不總是成功的情況之一。

+1

這是更長的一點:1.4引入了一個更新的集合框架與java.util.Collections.synchronizedMap和朋友,推薦使用,而不是哈希表。然後1.5引入了整個併發包。 但是Hashtable仍然圍繞着標準庫,並且可能會越來越多...... 「Java方法」似乎是同步是一個實現細節,並且它的需求應該通過文檔而不是通過API聲明來傳遞。 – araqnid 2009-10-19 21:36:14

+0

借調。這兩個類都是歷史的,我相信它們可以追溯到Java 1.0。我相信他們可以追溯到1.2,但沒有看到古代API在線。 – 2009-10-19 21:37:48

+0

@araqnid,「Java方法」的方式塗有血跡和多年來的表現,併發等傷疤。特別是從服務器端的使用起步,與簡單的「新Vector()」相比,該規模已經傾向於依賴注入,代碼到界面,架構宇航員的方式。 – 2009-10-19 21:54:10

1

你在掙扎的是,在多線程環境中,客戶端不能天真地使用具有可變的共享狀態的對象。收集界面本身並不告訴你如何安全地使用該對象。返回ConcurrentMap有助於提供一些額外的信息,但僅限於該特定情況。

通常,您必須在文檔中分別傳達線程安全問題(例如,javadoc)或使用自定義註釋,如Java Concurrency in Practice.中所述。返回對象的客戶端必須使用自己的鎖定機制或您自己的鎖定機制提供。界面通常與線程安全性正交。

如果客戶知道,所有的實現都是從並行實施,但這些信息不是由接口本身傳達這不是一個問題。