[英]What are the impacts of using Object.class lock in synchronized block?
我正在嘗試從多個類中的多個方法同步對xml文件的讀寫操作。 為此,我通過使用類級別鎖定來同步此操作。
代碼示例:
Test1.java:
public class Test1 {
public void method1() {
synchronized(CommonUtility.class) {
SomeObject someObject = testDAO.readSomething();
.
.
.
testDAO.writeSomething(someObject);
}
}
}
Test2.java
public class Test2 {
public void method2() {
synchronized(CommonUtility.class) {
SomeObject someObject = testDAO.readSomething();
.
.
.
testDAO.writeSomething(someObject);
}
}
}
要實現此類鎖,請考慮以下示例代碼:
synchronized(CommonUtility.class) {
.
.
.
}
使用Object.class代替CommonUtility.class有什么影響,例如:
synchronized(Object.class) {
.
.
.
}
我認為這兩種方法都不理想。
首先,這在這里建議:
您可能想知道當調用靜態同步方法時會發生什么,因為靜態方法與類而不是對象相關聯。 在這種情況下,線程獲取與該類關聯的Class對象的固有鎖定。
換句話說:當您使用synchronized(CommonUtility.class)
時,您將與CommonUtility中的所有靜態synchronized
方法隱式地“同步”。 更糟糕的是:想象一下,今天的類沒有這樣的方法。 但是下周,有人假設只有對該方法的調用會通過該監視器,從而在該實用工具類中添加了此類靜態synchronized
方法。 最壞的情況是,這可能會導致一些丑陋的(僅限運行時)意外。
然后:擴大范圍(使用Object.class)會使情況變得更糟。
我的回答:首先避免使用類對象。
IMO,“類級別”鎖定和“對象級別”鎖定的想法令人分心。 Java中只有一種基礎的同步機制: synchronized (o) { ... }
,其中o
可以是任何Java對象(請注意,在Java MyClass.class
是一個對象。)
當你寫的時候
synchronized SomeType foobar(...) { ... }
這實際上只是將實例用作保護其自己的成員變量的鎖定對象的快捷方式。
SomeType foobar(...) {
synchronized (this) {
...
}
}
所謂的“類級別”鎖定也是如此:這只是將類本身用作保護其自己的靜態成員的鎖定對象的一種簡便方法。
說到...
好的做法是將鎖對象保持在它保護的數據附近(對於“近”的某種定義)。 如果數據是private
,則鎖定對象應該是private
。 如果數據是某個實例的成員,則鎖對象應該是同一實例的成員,依此類推。
Object.class
並不是特別“接近”任何東西。 它會工作 ,以及任何其他對象,但使用它使你的代碼更難理解,因為讀者會花時間琢磨是什么促使您選擇Object.class
,並想知道你的選擇是否是基於一種誤解。
為了保護實例成員,我個人的偏好如下所示:
class MyClass {
private final Object lock = new Object();
private SomeType someVariableThatNeedsProtection = ...;
private SomeOtherType someOtherVariableThatAlsoNeedsProtection...;
...
public ... foobar(...) {
synchronized(lock) {
...
}
}
}
而且,如果我需要保護static
成員:
...
private static final Object lock = new Object();
...
lock
變量是private
,就像它保護的數據一樣。 任何想了解您的代碼的人都無需花時間搜索受同一鎖對象保護的其他任何東西,因為他們知道無法從MyClass
方法外部訪問它。
lock
變量也是final
。 這樣可以使讀者不必檢查代碼以確保它始終與用作鎖的對象相同 。 (提示:如果您認為您需要能夠分配lock
變量,那么您正在做的事情可能超出許多程序員的舒適度,或者犯了一個嚴重的錯誤。)
我正在嘗試從多個類的多個方法同步XML文件的讀寫操作。 為此,我通過使用類級別鎖來同步此操作。
那不是一個好主意。 您應該只有一個類(可能是XmlHelper
)來管理XML文件並進行鎖定。 XmlHelper
將在多個類的多個方法中使用,並將控制文件的鎖定,而不必擔心多個類。 那是更好的對象設計。
也許像這樣:
public class XmlHelper {
public XmlHelper(File xmlFile) {
public synchronized SomeObject readSomething() { ... }
public synchronized void writeSomething(SomeObject someObject) { ... }
}
然后,您的Test1
和Test2
類必須共享XmlHelper
類的相同實例,因此它們的鎖將相互阻塞。 實例級鎖並不總是一個好主意,因為應該盡可能細化鎖,但是在您的應用程序中它很好,因為XmlHelper
設計用於多個類來鎖定其IO操作。
使用
Object.class
代替CommonUtility.class
什么影響,例如:
正如其他人提到的那樣,對類的鎖定與調用synchronized static
方法相同。 由於鎖的紋理非常粗糙,因此應非常謹慎地使用此模式。 如果您的程序需要同時讀取/寫入2個XML文件怎么辦? 您的類級別鎖定將導致對2個文件的IO操作彼此阻塞-並非最佳。
如果您鎖定了Object.class
那么其他任何正在執行相同鎖定的類都將不必要地阻塞您的線程。 願上帝保佑你。
比賽條件受到打擊。 例如:thread1讀取文件內容並更新讀取的內容。 在線程1寫回文件之前,線程2讀取內容。 然后線程1將更新的內容寫入文件。 最后,thread2將內容寫入文件。 這導致內容丟失,
有兩種方法可以做到這一點。 您可以在XmlHelper
類上使用某種更新方法:
public synchronized void updateObject(...) {
SomeObjecto obj = readSomething();
// adjust object here based on the arguments
writeSomething(obj);
}
如果每個線程都需要執行自己的更新,那么它們將需要在外部鎖定同一對象。 我建議鎖定XmlHelper
的共享實例。
synchronized (xmlHelper) {
...
}
對類對象的鎖定將起作用,但是建議不要使用錘子。 同樣,如果您有2個XmlHelper
實例在2個不同的文件上運行,則您不希望對2個不同的文件進行IO操作來互相阻止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.