[英]Java Synchronization Lock
當我們說使用synchronized關鍵字鎖定一個對象時,它是否意味着我們正在獲取整個對象的鎖定或僅獲取塊中存在的代碼?
在以下示例中, listOne.add
是同步的,是否意味着如果另一個線程訪問listOne.get
,它將被阻塞,直到第一個線程離開此塊為止? 如果第二個線程在第一個線程仍在同步塊中時訪問同一對象的實例變量上的listTwo.get
或listTwo.add
方法,該怎么辦?
List<String> listONe = new ArrayList<String>();
List<String> listTwo = new ArrayList<String>();
/* ... ... ... */
synchronized(this) {
listOne.add(something);
}
鎖定在您包含在synchronized塊中的對象實例上。
但要小心! 該對象本質上不會被其他線程鎖定。 只有執行相同的線程synchronized(obj)
,其中obj在你的例子中是this
但在其他線程中也可以是變量引用,等待該鎖定。
因此,不執行任何同步語句的線程可以訪問“鎖定”對象的任何和所有變量,並且您可能會遇到競爭條件。
鑒於方法:
public void a(String s) {
synchronized(this) {
listOne.add(s);
}
}
public void b(String s) {
synchronized(this) {
listTwo.add(s);
}
}
public void c(String s) {
listOne.add(s);
}
public void d(String s) {
synchronized(listOne) {
listOne.add(s);
}
}
你不能同時調用a和b,因為它們被鎖定在同一個鎖上。 但是,您可以同時調用a和c(顯然有多個線程),因為它們沒有鎖定在同一個鎖上。 這可能會導致listOne出現問題。
您也可以同時調用a和d,因為d在此上下文中與c沒有區別。 它不使用相同的鎖。
始終使用相同的鎖鎖定listOne非常重要,並且在沒有鎖定的情況下不允許訪問它。 如果listOne和listTwo以某種方式相關並且有時需要同時/原子地更新,則需要一個鎖來訪問它們。 否則2個單獨的鎖可能會更好。
當然,如果您需要的是並發列表,您可能會使用相對較新的java.util.concurrent類:)
如果在同一實例上有同步塊,則其他線程將僅阻止。 因此,列表本身的任何操作都不會阻止。
synchronized(this) {
只會鎖定對象this
。 鎖定和使用對象listOne
:
synchronized(listOne){
listOne.add(something);
}
以便多個線程一次訪問一個listOne。
請參閱: http : //download.oracle.com/javase/tutorial/essential/concurrency/locksync.html
您需要了解鎖定是建議性的,並且沒有實際執行。 例如,如果您決定使用Object
來鎖定對某些類字段的訪問權限,則必須以這樣的方式編寫代碼,以便在訪問這些字段之前實際獲取鎖定。 如果不這樣做,您仍然可以訪問它們並可能導致死鎖或其他線程問題。
例外情況是在方法上使用synchronized
關鍵字,運行時將自動為您獲取鎖定而無需執行任何特殊操作。
Java語言規范定義了synchronized
語句的含義,如下所示:
synchronized
語句代表執行線程獲取互斥鎖(第17.1節),執行塊,然后釋放鎖。 當執行線程擁有鎖時,沒有其他線程可以獲取鎖。SynchronizedStatement:` synchronized ( Expression ) Block`
Expression的類型必須是引用類型,否則會發生編譯時錯誤。
通過首先評估表達式來執行同步語句。
如果表達式的評估由於某種原因突然完成,則同步語句由於同樣的原因而突然完成。
否則,如果Expression的值為null,則拋出NullPointerException。
否則,讓表達式的非空值為V.執行線程鎖定與V關聯的鎖。然后執行塊。 如果塊的執行正常完成,則解鎖並且synchronized語句正常完成。 如果塊的執行因任何原因突然完成,則鎖定被解鎖,然后同步語句突然完成,原因相同。
獲取與對象關聯的鎖本身並不會阻止其他線程訪問對象的字段或在對象上調用未同步的方法。 其他線程也可以以傳統方式使用同步方法或同步語句來實現互斥。
也就是說,在你的例子中
synchronized(this) {
listOne.add(something);
}
synchronized塊確實以任何特殊方式處理listOne
引用的對象,其他線程可以listOne
使用它。 然而,它確保沒有其他線程可以進入同步塊用於通過引用的對象this
同時。 因此,如果使用listOne
所有代碼都在同一對象的同步塊中,則在任何給定時間最多一個線程可以與listOne
一起使用。
另請注意,被鎖定的對象沒有特別保護其狀態的並發訪問,因此代碼
void increment() {
synchronized (this) {
this.counter = this.counter + 1;
}
}
void reset() {
this.counter = 0;
}
錯誤地同步,因為第二個線程可能在第一個線程已讀取但尚未寫入counter
執行reset
,從而導致重置被覆蓋。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.