注意:在理解synchronized時,要知道一個核心點,synchronized鎖定的不是代碼,而是對象。使用synchronized時,其會申請對象的堆內存,進行鎖定。java
Object o = new Object(); // 鎖對象 public void test01(){ //任何線程要執行如下的代碼,必須先拿到鎖 synchronized (o){ // doSomething... } }
上述寫法是建立一個鎖對象,其實能夠自身做爲鎖對象。安全
public void test02(){ synchronized (this){ // doSomething... } }
同寫法二。ide
public synchronized void test03(){ // doSomething... }
鎖定靜態方法。靜態方法是屬於類方法,沒有對象。測試
public synchronized static void test04(){ }
同寫法四this
public static void test04(){ synchronized (SynchronizeTest.class){ } }
演示代碼:線程
public class RunnableDemo implements Runnable { private int count = 10; @Override public /*synchronized*/ void run() { count--; System.out.println(Thread.currentThread().getName() + "count=" + count); } public static void main(String[] args) { RunnableDemo t = new RunnableDemo(); for (int i = 0; i < 8; i++) { new Thread(t, "結果是:" + i).start(); } } }
結果是:調試
結果是:1count=9 結果是:0count=8 結果是:2count=7 結果是:3count=6 結果是:5count=5 結果是:4count=4 結果是:6count=3 結果是:7count=2
加入synchronized:code
結果是:0count=9 結果是:1count=8 結果是:2count=7 結果是:3count=6 結果是:4count=5 結果是:5count=4 結果是:6count=3 結果是:7count=2
能夠看到輸出順序是不同的.對象
在實際業務場景中,每每將讀方法不加鎖,寫的方法加鎖,這樣會致使一個問題,也就是讀的數據是寫以前的數據,致使髒讀問題。解決方案就是,在讀方法上也加鎖。內存
加鎖方法不影響不加鎖方法的執行;
加鎖方法訪問另一個加鎖方法,一個線程擁有某個對象的鎖,再次申請的時候能夠再次獲得這把鎖(至關於鎖上了兩把一樣的鎖);子類的同步方法調用父類的同步方法也能夠;
synchronized 遇到異常,鎖會被釋放。若是不想該鎖被釋放,就直接catch;
public class StringAsSynchObject { private String stra = "hello"; private String strb = "hello"; void test1(){ synchronized (stra){ } } void test2(){ synchronized (strb){ } } }
在上述代碼中,由於stra和strb 鎖的是同一個對象,若是用到了同一個類庫,在該類庫中的代碼鎖定了字符串"hello",咱們讀不到源碼,而在業務代碼中也鎖定了一樣的字符串,這就有可能會形成很是詭異的死鎖阻塞。由於程序和類庫不經意用了同一把鎖。(這種狀況通常沒辦法調試)。因此一般不要字符串做爲鎖定對象。
synchronize 鎖釋放的狀況:
1)線程執行完畢;
2)線程發生異常;
3)線程進入休眠狀態。wait()和notify()/notifyAll() 與 synchronize同時出現。