synchronized關鍵字修飾的方法實現同步
1.在方法級別 public synchronized ….
內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。
注: synchronized關鍵字也可以修飾靜態方法,此時如果調用該靜態方法,將會鎖住整個類
2.同步代碼塊 synchronized(對象){}
即有synchronized關鍵字修飾的語句塊。
通常沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼即可。
/** * 用同步代碼塊實現 * * @param money */ public void save1(int money) { synchronized (this) { account += money; } }
1.當synchronized作用在方法上的時候,鎖住的就是這個對象的實例 synchronized(this).
2.當一個線程訪問synchronized(this) 同步塊時, 另一個線程仍然可以訪問當前對象內的非synchroinzed(this)同步塊代碼
3.同步是一個耗性能的操作,因此我們盡量減少同步的內容,最好不要加載整個方法上
3.給對象加鎖
當有一個明確的對象作為鎖時,就可以用類似下面這樣的方式寫程序。
public void method3(SomeObject obj) { //obj 鎖定的對象 synchronized(obj) { // todo } }
用synchronized 給account對象加了鎖。這時,當一個線程訪問account對象時,其他試圖訪問account對象的線程將會阻塞,直到該線程訪問account對象結束。也就是說誰拿到那個鎖誰就可以運行它所控制的那段代碼。
4.修飾一個靜態的方法
public synchronized static void method() { // todo }
靜態方法是屬於類的而不屬於對象的。同樣的,synchronized修飾的靜態方法鎖定的是這個類的所有對象。
當synchronized修飾靜態方法的時候, 同步對象就是這個類的類對象。
如代碼中的例子,當第一個線程進入method1的時候,需要占用TestReflection.class才能執行。
第二個線程進入method2的時候進去不,只有等第一個線程釋放了對TestReflection.class的占用,才能夠執行。 反推過來,第二個線程也是需要占用TestReflection.class。 那么TestReflection.class就是method2的同步對象。
5. 修飾一個類
class ClassName { public void method() { synchronized(ClassName.class) { // todo } } }
給class加鎖和上例的給靜態方法加鎖是一樣的,所有對象公用一把鎖
第二種:(volatile)實現線程同步
volatile關鍵字為域變量的訪問提供了一種免鎖機制
只需在account前面加上volatile修飾,即可實現線程同步。
class Bank { //需要同步的變量加上volatile private volatile int account = 100; public int getAccount() { return account; } //這里不再需要synchronized public void save(int money) { account += money; } }
1.多線程中的非同步問題主要出現在對域的讀寫上,如果讓域自身避免這個問題,則就不需要修改操作該域的方法。
2.volatile不能保證原子操作,因此volatile不能代替synchronized
3.每次要線程要訪問volatile修飾的變量時都是從內存中讀取,而不是從緩存當中讀取,因此每個線程訪問到的變量值都是一樣的。這樣就保證了同步。