synchronized在JDK5以前一直被稱爲重量級鎖,是一個較爲雞肋的設計,而在JDK6對synchronized內在機制進行了大量顯著的優化,加入了CAS,輕量級鎖和偏向鎖的功能,性能上已經跟ReentrantLock相差無幾,並且synchronized在使用上更加簡單,不易出錯(避免哲學家就餐問題形成的死鎖),所以若是僅僅是爲了實現互斥,而不須要使用基於Lock的附加屬性(中斷、條件等),推薦優先使用synchronized。
html
synchronized有同步方法和同步語句塊兩種形式,分鎖定對象和做用域兩個維度,當鎖定的對象進入做用域時,互斥纔會生效。一個線程訪問擁有相同鎖定對象時,進入做用域時必須以串行方式進行。修飾的對象有:修飾普通方法(又稱爲同步方法),修飾靜態方法,修飾對象實例,修飾class literals(類名稱字面常量)。java
又稱同步方法,鎖定的是調用這個同步方法對象。一個線程訪問實例對象中的synchronized代碼塊時,其餘試圖訪問該對象synchronized修飾的區域的線程將阻塞。同一個對象共享一把鎖,同一時間只能有一個synchronized區域能夠得到鎖,能夠稱爲對象鎖。須要注意兩點:(1)做用只會在同一個實例上,(2)對象中的全部synchronized修飾的區域。例如:對於fooA對象,線程t1,t2訪問methodA是互斥的,線程t1,t2分別訪問methodA,methodB一樣是互斥的;當訪問methodA時,methodB 不能夠訪問,methodC能夠訪問。git
1 public class Foo implements Runnable { 2 public static synchronized void methodA() { 3 //TODO 4 } 5 6 public static synchronized void methodB() { 7 //TODO 8 } 9 10 public void methodC() { 11 //TODO 12 } 13 14 public static void main(String[] args) { 15 Foo fooA = new Foo(); 16 Foo fooB = new Foo(); 17 18 Thread t1 = new Thread(fooA, "t1"); 19 Thread t2 = new Thread(fooB, "t2"); 20 t1.start(); 21 t2.start(); 22 } 23 }
修飾靜態方法 github
鎖定是靜態方法所屬的類,此時該類共用一把鎖,能夠稱爲類鎖。一個線程訪問synchronized靜態方法時,其餘試圖訪問該類synchronized修飾的區域的線程都將阻塞,而非synchronized修飾的區域不會阻塞。修飾靜態方法是修飾普通方法的變種版,前者鎖定該類(跟對象不要緊),後者指鎖定當前對象。例如:線程t1,t2分別訪問methodA,methodB一樣是互斥的。多線程
修飾對象實例的狀況與修飾普通方法一致,區別在於只有線程進入同步語句塊時,纔是互斥的。而修飾class literals(類名稱字面常量)與修飾靜態方法一致。例如:methodD鎖定的是當前對象,而methodE中的synchronized鎖定的是Foo類的全部對象。性能
1 public class Foo { 2 public void methodD() { 3 synchronized (this) { 4 //TODO 5 } 6 } 7 8 public void methodE() { 9 synchronized (Foo.class) { 10 //TODO 11 } 12 } 13 }
synchronized機制 優化
synchronized是Java爲線程間通訊提供的一種方式,synchronized的這種同步機制是互斥鎖機制。實現互斥的方式有:臨界區(Critical Section),互斥量(Mutex),信號量(Semaphores)和事件(Event)。ui
synchronized是採用臨界區的方式實現互斥的,經過對多線程的串行化來訪問公共資源或一段代碼,保證同一時刻只有一個線程能訪問臨界區內共享數據。若是多個線程試圖同時訪問臨界區,其中一個線程搶佔臨界區後,其餘試圖訪問該臨界區的線程將會掛起,直到進入臨界區的線程。舉個栗子,會議室資源相對緊張,有多個團隊都須要使用會議室,這時就看哪一個團隊的迅速了。當這個團隊搶佔到會議室後,就會掛起「正在會議中」的提示牌,其餘須要使用會議室的人看到會等待,直到當前使用會議室的團隊使用完,移走「正在會議中」的提示牌。this
synchronized同時保證了可見性。互斥鎖釋放後,確保一個線程修改的對象狀態,對其餘線程是可見的,即在隨後獲取鎖的線程中保證得到最新共享資源的狀態。synchronized互斥鎖是經過監視器(Monitor)實現的。spa
監視器是一種同步機制。 經過獲取和釋放鎖,保證線程間能夠互斥地操做共享資源,同一時間最多隻有一個線程佔用監視器,即互斥(mutex);採用條件變量(Condition Variables),容許線程阻塞和等待另外一個線程發送信號的方法彌補互斥鎖的不足,這就是監視器的互斥和協做。
當多線程同時訪問一段同步代碼時,新請求的線程會首先加入到Entry Set集合中,經過競爭(compete)方式,同一時間只有一個線程能夠競爭成功並獲取監視器,進入The Owner。獲取監視器的線程調用wait()後就會釋放監視器,並進入Wait Set集合中等待知足條件時被喚醒。不管是acquire仍是release都是使用原子指令如test-and-set、compare-and-set來實現自旋鎖來保證對關鍵段的訪問。