[英]Couple of questions regarding the synchronized keyword in Java (and C#'s lock)
Java中的以下類是否相同? 如果不是,為什么?
class Abc { private int c = 0;
\n\n }\n }\n\n\n class Def {\n private int c = 0;\n private final Object lock = new Object();\n\n\npublic synchronized void add(int a) { c += a; } public synchronized void subtract(int a) { c -= a;
\n\n }\n }\npublic void add(int a) { synchronized(lock) { c += a; } } public void subtract(int a) { synchronized(lock) { c -= a; }
另外,在Def
會出現什么問題,使用this
作為synchronized參數而不是lock
? 以下是問題嗎?
Def def = new Def() synchronized (def) { def.add(5); //originates deadlock? or is this allowed AS LONG //as all this happens in the same thread? }
Java中的synchronized語句就像C#的鎖語句一樣嗎? 如果不是,他們的區別是什么? 如果是的話,為什么C#也不允許鎖定方法,就像Java允許的那樣?
所以我猜可以通過以下示例描述synchronized (this)
的問題?
class Xyz { private int c;
\n\n }\npublic void add(int a) { synchronized(this) { c += a; } } public void subtract(int a) { synchronized(this) { c -= a; } } public void show666() { return 666; }
xyz.add(0)
調用xyz.add(0)
,同時Thread2嘗試調用xyz.show666()
。 Thread2必須等待xyz.add(0)
完成xyz.add(0)
盡管它不需要任何與鎖直接相關的信息。 是嗎?
不,他們是不同的。 使用synchronized(reference){}
獲取給定引用(鎖定對象)上的監視器鎖定,並在方法聲明中使用synchronized
將this
用於監視器鎖定。 最終結果是,外部調用者無法獲得與add
和subtract
方法相同的鎖定。
不存在死鎖,您可以保存任意數量的監視器鎖,並且Def
對象使用不同的對象來鎖定。 但即使是第一類Abc
也不會陷入僵局。 java中的同步鎖是重要的。 因此,您可以在理論上嘗試將它們鎖定在同一個線程中,並且可以根據需要多次鎖定它們。
令人驚訝的是,C#不允許在方法上使用lock
,但是當在引用上使用時,它似乎與java synchronized
關鍵字相似。 C#的設計目標之一是足夠熟悉Java開發人員能夠在沒有完全替換思想的情況下接收它,所以我想這並不太令人驚訝。
1 Java中的以下類是否相同? 如果不是,為什么?
它們因同步的實例而不同,因此會受到這些實例之間差異的影響。
類Abc
使用調用該方法的Abc
實例。 其他具有訪問實例的代碼也可以通過顯式使用synchronized塊中的實例來使用它進行同步。
Class Def
的同步代碼塊顯式命名與它們同步的實例。 因為這種情況下是私有的Def
情況下,外部代碼以Def
不能用它來同步(除非該實例以某種方式泄露)。
有些人可能認為Def
方法更安全 ,因為您應該使用私有實例變量的同樣原因來封裝您的鎖是很重要的。
最后,synchronized關鍵字和使用synchronized語句之間存在差異,例如synchronized語句可以鎖定任何對象,而不僅僅是執行代碼的實例,synchronize關鍵字稍微更有效等等。
2a另外,在Def中會出現什么問題,使用它作為同步參數而不是鎖定?
沒問題。 上的實例方法使用同步關鍵字在語義上等同於包裹方法的代碼在上同步的同步塊this
(synchronized關鍵字是稍微更有效)。 在靜態方法上使用synchronized關鍵字與使用在類本身上同步的同步塊(例如synchronized(FooBar.class) { ... }
)相同。
2b以下問題是什么?
不,Java中的鎖是可重入的,這意味着持有保護實例上的鎖的線程可以進入和退出在同一實例上同步的任何其他代碼塊。
3a Java中的同步語句就像C#的鎖定語句一樣嗎? 如果不是,他們的區別是什么?
語義上等同。
但請注意關於Monitor.Enter
和Monitor.Exit
答案
3b如果是,為什么C#也不允許鎖定方法,就像Java允許的那樣?
它確實 - 使用[MethodImpl(MethodImplOptions.Synchronized)]
注釋。
4 Thread1調用xyz.add(0),同時Thread2嘗試調用xyz.show666()。 Thread2必須等待Thread1完成xyz.add(0),盡管它不需要任何與鎖直接相關的信息。 是嗎?
不,很多人認為“鎖定”實例會影響整個實例。 它僅影響在該實例上同步的同步代碼(同步語句中的同步方法和代碼)。 非同步代碼不受影響(至少在它遇到同步語句或調用同步方法之前)。
未同步的方法show666()
不會導致線程阻塞。 如果將synchronized(this)
語句更改為synchronized
方法,則沒有任何更改 - show666
也不會阻塞(除非它同步)。
它們幾乎是等價的,只有一點區別:因為Def
使用內部私有對象作為鎖,沒有其他人可以從外部世界鎖定該對象。 在Abc
對象的情況下,其他人可能鎖定對象然后從不同的線程調用它的一些方法,這可能導致死鎖。 實際的可能性是微弱的(因為它顯然需要代表另一個程序員的一些惡作劇或無知),但不是零。 因此,有些人更喜歡Def
風格是安全的,盡管AFAIK常見的習語與Abc
。
Java鎖是可重入的,因此從同一個線程多次調用鎖是可以的。
對不起,我不能勝任C#。
你的意思是代碼示例中的synchronized(this)
而不是synchronized(lock)
嗎? 無論如何,由於show666
未同步,因此對它的調用不會阻塞,即使對同步方法的另一次調用也會阻塞同一對象。
請參閱http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html
它應該可以幫助你回答1和2,特別是它說:
可重入同步
回想一下,線程無法獲取另一個線程擁有的鎖。 但是一個線程可以獲得它已經擁有的鎖。 允許線程多次獲取相同的鎖可啟用重入同步。 這描述了一種情況,其中同步代碼直接或間接地調用也包含同步代碼的方法,並且兩組代碼使用相同的鎖。 在沒有可重入同步的情況下,同步代碼必須采取許多額外的預防措施,以避免線程導致自身阻塞。
據我所知,除非鎖被其他人同步,否則這兩個類幾乎與外界相當。
由於可重入同步,不應該導致任何特定問題
(3.不熟悉C#)
1.一般來說,它們是不同的,因為不同對象的監視器用於同步模塊。 順便說一句,如果你不使用abc類實例的監視器(特別是如果你不在abc類之外使用synchronized(abcObject){},那么這兩個類將表現相同),其中abcObject是Abc類的實例)
2.A. 以下兩種方法是相同的:
public void add(int a) {
synchronized(this) {
c += a;
}
}
和
public synchronized void add(int a) {
c += a;
}
2.B. 我沒有在該代碼中看到任何問題:兩個監視器(def實例的監視器和def.lock實例的監視器)總是按嚴格順序鎖定(沒有循環等待條件)
3.不能對C#說什么
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.