Java synchronized 詳解


下面的文字均來自其它博客和網頁。

參考:http://www.jianshu.com/p/ea9a482ece5f

由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。
需要明確的幾個問題:

  • synchronized關鍵字可以作為函數的修飾符,也可作為函數內的語句,也就是平時說的同步方法和同步語句塊。如果 再細的分類,synchronized可作用於instance變量、object reference(對象引用)、static函數和class literals(類名稱字面常量)身上。
  • 無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其他線程的對象訪問。
  • 每個對象只有一個鎖(lock)與之相關聯。
  • 實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。

synchronized關鍵字的作用域有二種:

  1. 某個對象實例內,synchronized aMethod(){}可以防止多個線程同時訪問這個對象的synchronized方法(如果一個對象有多個synchronized方法,只要一個線 程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)。這時,不同的對象實例的 synchronized方法是不相干擾的。也就是說,其它線程照樣可以同時訪問相同類的另一個對象實例中的synchronized方法;
  2. 某個類的范圍,synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法。它可以對類的所有對象實例起作用。

synchronized 方法

每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多只有一個處於可執行狀態(因為至多只有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變量的訪問。

synchronized 方法的缺陷

同步方法,這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象 P1在不同的線程中執行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的Class所產生的另一對象P2卻可以任意調用這個被加 了synchronized關鍵字的方法.同步方法實質是將synchronized作用於object reference。――那個拿到了P1對象鎖的線程,才可以調用P1的同步方法,而對P2而言,P1這個鎖與它毫不相干,程序也可能在這種情形下擺脫同步機制的控制,造成數據混亂:(
;若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。

synchronized 代碼塊

除了方法前用synchronized關鍵字,synchronized關鍵字還可以用於方法中的某個區塊中,表示只對這個區塊的資源實行互斥訪問。用法是: synchronized(this){/區塊/},它的作用域是當前對象。
這時鎖就是對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的instance變量(它得是一個對象)來充當鎖:

class Foo implements Runnable { private byte[] lock = new byte[0]; // 特殊的instance變量 Public void methodA() { synchronized(lock) { //… } } //….. }

注:零長度的byte數組對象創建起來將比任何對象都經濟――查看編譯后的字節碼:生成零長度的byte[]對象只需3條操作碼,而Object lock = new Object()則需要7行操作碼。

synchronized 靜態方法

將synchronized作用於static 函數,示例代碼如下:

Class Foo {
  // 同步的static 函數 public synchronized static void methodAAA() { //…. } public void methodBBB() { synchronized(Foo.class) // class literal(類名稱字面常量) } }

代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數產生的效果是一樣的,取得的鎖很特別,是當前調用這個方法的對象所屬的類(Class,而不再是由這個Class產生的某個具體對象了)。

可以推斷:如果一個類中定義了一個synchronized 的 static 函數A,也定義了一個 synchronized 的 instance函數B,那么這個類的同一對象Obj在多線程中分別訪問A和B兩個方法時,不會構成同步,因為它們的鎖都不一樣。B方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class。

 

文/DanieX(簡書作者)
原文鏈接:http://www.jianshu.com/p/ea9a482ece5f
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。

下面是另一篇博客,寫得也不錯。

來自: http://zhangjunhd.blog.51cto.com/113473/70300/

在並發環境下,解決共享資源沖突問題時,可以考慮使用鎖機制。

1. 對象的鎖
所有對象都自動含有單一的鎖。
JVM負責跟蹤對象被加鎖的次數。如果一個對象被解鎖,其計數變為0。在任務(線程)第一次給對象加鎖的時候,計數變為1。每當這個相同的任務(線程)在此對象上獲得鎖時,計數會遞增。
只有首先獲得鎖的任務(線程)才能繼續獲取該對象上的多個鎖。
每當任務離開一個synchronized方法,計數遞減,當計數為0的時候,鎖被完全釋放,此時別的任務就可以使用此資源。
2.synchronized 同步塊
2.1 同步到單一對象鎖
當使用同步塊時,如果方法下的同步塊都同步到一個對象上的鎖,則所有的任務(線程)只能互斥的進入這些同步塊。
Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不同的方法的同步塊中,雖然這些同步塊處在不同的方法中,但由於是同步到同一個對象(當前對象  synchronized ( this)),所以對它們的方法依然是互斥的。
Resource1.java
package com.zj.lock;
import java.util.concurrent.TimeUnit;
 
public  class Resource1 {
     public  void f() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in f()");
        synchronized ( this) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in f()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  void g() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in g()");
        synchronized ( this) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in g()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  void h() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in h()");
        synchronized ( this) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in h()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  static  void main(String[] args) {
        final Resource1 rs =  new Resource1();
 
        new Thread() {
            public  void run() {
              rs.f();
           }
       }.start();
 
        new Thread() {
            public  void run() {
              rs.g();
           }
       }.start();
 
       rs.h();
    }
}
結果:
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
Thread-1:not synchronized in g()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
2.2  同步到多個對象鎖
Resource1.java演示了三個線程(包括main線程)試圖進入某個類的三個不同的方法的同步塊中,這些同步塊處在不同的方法中,並且是同步到三個不同的對象( synchronized ( this), synchronized(syncObject1), synchronized (syncObject2)),所以對它們的方法中的臨界資源訪問是獨立的。
Resource2.java
package com.zj.lock;
import java.util.concurrent.TimeUnit;
 
public  class Resource2 {
     private Object syncObject1 =  new Object();
     private Object syncObject2 =  new Object();
 
     public  void f() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in f()");
        synchronized ( this) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in f()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  void g() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in g()");
        synchronized (syncObject1) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in g()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  void h() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in h()");
        synchronized (syncObject2) {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in h()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }
    }
 
     public  static  void main(String[] args) {
        final Resource2 rs =  new Resource2();
 
        new Thread() {
            public  void run() {
              rs.f();
           }
       }.start();
 
        new Thread() {
            public  void run() {
              rs.g();
           }
       }.start();
 
       rs.h();
    }
}
結果:
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
main:synchronized in h()
Thread-1:not synchronized in g()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
3.Lock 對象鎖
除了使用synchronized外,還可以使用Lock對象來創建臨界區。Resource3.java的演示效果同Resource1.java;Resource4.java的演示效果同Resource2.java。
Resource3.java
package com.zj.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public  class Resource3 {
     private Lock lock =  new ReentrantLock();
 
     public  void f() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in f()");
       lock.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in f()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock.unlock();
       }
    }
 
     public  void g() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in g()");
       lock.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in g()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock.unlock();
       }
    }
 
     public  void h() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in h()");
       lock.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in h()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock.unlock();
       }
    }
 
     public  static  void main(String[] args) {
        final Resource3 rs =  new Resource3();
 
        new Thread() {
            public  void run() {
              rs.f();
           }
       }.start();
 
        new Thread() {
            public  void run() {
              rs.g();
           }
       }.start();
 
       rs.h();
    }
}
結果:
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
Thread-1:not synchronized in g()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Resource4.java
package com.zj.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public  class Resource4 {
     private Lock lock1 =  new ReentrantLock();
     private Lock lock2 =  new ReentrantLock();
     private Lock lock3 =  new ReentrantLock();
 
     public  void f() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in f()");
       lock1.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in f()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock1.unlock();
       }
    }
 
     public  void g() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in g()");
       lock2.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in g()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock2.unlock();
       }
    }
 
     public  void h() {
       // other operations should not be locked...
       System. out.println(Thread. currentThread().getName()
              + ":not synchronized in h()");
       lock3.lock();
        try {
            for ( int i = 0; i < 5; i++) {
              System. out.println(Thread. currentThread().getName()
                     + ":synchronized in h()");
               try {
                  TimeUnit. SECONDS.sleep(3);
              }  catch (InterruptedException e) {
                  e.printStackTrace();
              }
           }
       }  finally {
           lock3.unlock();
       }
    }
 
     public  static  void main(String[] args) {
        final Resource4 rs =  new Resource4();
 
        new Thread() {
            public  void run() {
              rs.f();
           }
       }.start();
 
        new Thread() {
            public  void run() {
              rs.g();
           }
       }.start();
 
       rs.h();
    }
}
結果:
Thread-0:not synchronized in f()
Thread-0:synchronized in f()
main:not synchronized in h()
main:synchronized in h()
Thread-1:not synchronized in g()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM