1.背景
前幾天線上項目出現一個問題,由於並發問題,導致服務器集群中的部分服務器中的數據沒有更新。經過review代碼,發現沒有進行數據的同步操作。最后使用synchronize解決了問題。解決問題后,在空余時間對synchronized的應用進行了研究。
2.synchronized介紹
synchronized是Java中的關鍵字,是一種同步鎖。它修飾的對象有以下幾種:
1. 修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號{}括起來的代碼,作用的對象是調用這個代碼塊的對象;
2. 修飾一個方法,被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用的對象是調用這個方法的對象;
3. 修改一個靜態的方法,其作用的范圍是整個靜態方法,作用的對象是這個類的所有對象;
4. 修改一個類,其作用的范圍是synchronized后面括號括起來的部分,作用主的對象是這個類的所有對象。
3. 示例
3.1 synchronized修飾代碼塊
如果多個線程訪問的是同一個實例對象,則會出現等待,如果多個線程訪問的都是不同的實例對象,則不會出現等待
3.1.1 synchronized(this)
代碼如下:
public class SyncBlock implements Runnable{
private static Integer count; public SyncBlock() { // TODO Auto-generated constructor stub count = 0; } public int getCount(){ return count; } @Override public void run() { // TODO Auto-generated method stub System.out.println("當前執行的程序:"+Thread.currentThread().getName()); synchronized(this) { for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args){ SyncBlock syncBlock = new SyncBlock(); Thread th1 = new Thread(syncBlock,"第一集團軍"); Thread th2 = new Thread(syncBlock,"第二集團軍"); th1.start(); th2.start(); } }
執行結果如下:

通過執行結果可以看出,同步鎖機制生效。synchronized(this)使得兩個線程同步等待。在main函數中增加三行代碼
public static void main(String[] args){
SyncBlock syncBlock = new SyncBlock();
SyncBlock syncBlock3 = new SyncBlock();
Thread th1 = new Thread(syncBlock,"第一集團軍");
Thread th2 = new Thread(syncBlock,"第二集團軍");
Thread th3 = new Thread(syncBlock3,"第三集團軍");
th1.start();
th2.start();
th3.start();
}
執行結果如下:

通過執行結果可以看出,線程1和線程2之間出現了等待,線程1執行完之后執行線程2,而線程3與線程1和2之間沒有等待。這是因為線程1和線程2是監視的同一個SyncBlock對象syncBlock,而線程3監視的是獨立的對象syncBlock3。也就是說,synchronized(this)對於線程3與線程1和2,鎖的是不同對象,所以彼此之間無法同步。
3.1.2 靜態變量synchronized(this.count)
鎖的對象是整型的實例對象count,如果count對象值是固定不變的,則所有線程之間都會等待,如果count是變化的,如count++,則所有線程之間都不會等待。因為count++的結果會改變count的對象引用,所以
synchronized(count)鎖定的是不同的實例對象,也就沒有起到鎖的作用。
修改上面的代碼如下:
1 public void run() { 2 // TODO Auto-generated method stub 3 System.out.println("當前執行的程序:"+Thread.currentThread().getName()); 4 synchronized(count) { 5 for (int i = 0; i < 5; i++) { 6 try { 7 System.out.println(Thread.currentThread().getName() + ":" + (count++)); 8 Thread.sleep(100); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 } 14 }
執行結果如下:

如果count的值不發生改變
1 public void run() { 2 // TODO Auto-generated method stub 3 System.out.println("當前執行的程序:"+Thread.currentThread().getName()); 4 synchronized(count) { 5 for (int i = 0; i < 5; i++) { 6 try { 7 System.out.println(Thread.currentThread().getName() + ":" + (count)); 8 Thread.sleep(100); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 } 14 }
執行結果如下:由於count值不發生變化,count是對多有實例對象共享,所以所有線程多會出現等待。

3.1.3 synchronized(A.class)
A.class表示類對象。每個類都對應着一個這樣的類對象,所有線程都會彼此間等待。
修改代碼如下:
1 public void run() { 2 // TODO Auto-generated method stub 3 System.out.println("當前執行的程序:"+Thread.currentThread().getName()); 4 synchronized(SyncBlock.class) { 5 for (int i = 0; i < 5; i++) { 6 try { 7 System.out.println(Thread.currentThread().getName() + ":" + (count++)); 8 Thread.sleep(100); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 } 14 }
執行結果如下:

3.2 synchronized非靜態方法
1 synchronized public void run() { 2 // TODO Auto-generated method stub 3 System.out.println("當前執行的程序:"+Thread.currentThread().getName()); 5 for (int i = 0; i < 5; i++) { 6 try { 7 System.out.println(Thread.currentThread().getName() + ":" + (count++)); 8 Thread.sleep(100); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 }14 }
執行結果如下,與在方法在使用synchronized(this)相同。多個線程訪問的是同一個實例對象,則會出現等待,如果多個線程訪問的都是不同的實例對象,則不會出現等待

3.4 synchronized靜態方法
靜態方法屬於類方法,不屬於任一實例對象。為所有實例對象所共享。因此對於所有線程調用synchronized靜態方法,彼此之間會出現等待。與synchronized(A.clsss)類似。只是作用范圍不同。
