複製鏈接
請複製以下鏈接發送給好友

synchronized

(Java語言的關鍵字)

鎖定
synchronized 關鍵字,代表這個方法加鎖,相當於不管哪一個線程(例如線程A),運行到這個方法時,都要檢查有沒有其它線程B(或者C、 D等)正在用這個方法(或者該類的其他同步方法),有的話要等正在使用synchronized方法的線程B(或者C 、D)運行完這個方法後再運行此線程A,沒有的話,鎖定調用者,然後直接運行。它包括兩種用法:synchronized 方法和 synchronized 塊。
Java語言的關鍵字,可用來給對象和方法或者代碼塊加鎖,當它鎖定一個方法或者一個代碼塊的時候,同一時刻最多隻有一個線程執行這段代碼。當兩個併發線程訪問同一個對象object中的這個加鎖同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。然而,當一個線程訪問object的一個加鎖代碼塊時,另一個線程仍可以訪問該object中的非加鎖代碼塊。
中文名
Java語言的關鍵字
外文名
synchronized
代    表
方法加鎖
方法聲明
放在範圍操作符
英式發音
英['sɪŋkrənaɪzd]
美式發音
美['sɪŋkrənaɪzd]

目錄

synchronized方法

synchronized 方法
1.方法聲明時使用,放在範圍操作符(public等)之後,返回類型聲明(void等)之前.這時,線程獲得的是成員鎖,即一次只能有一個線程進入該方法,其他線程要想在此時調用該方法,只能排隊等候,當前線程(就是在synchronized方法內部的線程)執行完該方法後,別的線程才能進入.
例如:
public synchronized void synMethod(){
//方法體
}
如在線程t1中有語句obj.synMethod(); 那麼由於synMethod被synchronized修飾,在執行該語句前, 需要先獲得調用者obj的對象鎖, 如果其他線程(如t2)已經鎖定了obj (可能是通過obj.synMethod,也可能是通過其他被synchronized修飾的方法obj.otherSynMethod鎖定的obj), t1需要等待直到其他線程(t2)釋放obj, 然後t1鎖定obj, 執行synMethod方法. 返回之前釋放obj鎖.
2.對某一代碼塊使用,synchronized後跟括號,括號裏是變量,這樣,一次只有一個線程進入該代碼塊.此時,線程獲得的是成員鎖.例如:
public Object synMethod(Object a1){
    synchronized(a1){
//一次只能有一個線程進入
  }
}
3.synchronized後面括號裏是一對象,此時,線程獲得的是對象鎖.例如:
public classMyThread implements Runnable{
    public static void main(Stringargs[]){
      MyThread mt=new MyThread();
        Thread t1=newThread(mt,"t1");
        Thread t2=newThread(mt,"t2");
        Thread t3=newThread(mt,"t3");
        Thread t4=newThread(mt,"t4");
        Thread t5=newThread(mt,"t5");
        Thread t6=newThread(mt,"t6");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
}   
public void run(){
    synchronized(this){
      System.out.println(Thread.currentThread().getName());
   }
}
對於3,如果線程進入,則得到當前對象鎖,那麼別的線程在該類所有對象上的任何操作都不能進行.在對象級使用鎖通常是一種比較粗糙的方法。為什麼要將整個對象都上鎖,而不允許其他線程短暫地使用對象中其他同步方法來訪問共享資源?如果一個對象擁有多個資源,就不需要只為了讓一個線程使用其中一部分資源,就將所有線程都鎖在外面。由於每個對象都有鎖,可以如下所示使用虛擬對象來上鎖:
class FineGrainLock{
 MyMemberClassx,y;
 Object xlock = new Object(), ylock = newObject();
 public void foo(){
     synchronized(xlock){
     //accessxhere
      }
     //dosomethinghere-butdon'tusesharedresources
      synchronized(ylock){
      //accessyhere
      }
 }
    public void bar(){
      synchronized(this){
         //accessbothxandyhere
     }
    //dosomethinghere-butdon'tusesharedresources
    }
}
4.synchronized後面括號裏是類,此時,線程獲得的是對象鎖.例如:
classArrayWithLockOrder{
 private static long num_locks=0;
 private long lock_order;
 private int[] arr;
 public ArrayWithLockOrder(int[]a){
     arr=a;
      synchronized(ArrayWithLockOrder.class){//-----這裏
          num_locks++;//鎖數加1。
           lock_order=num_locks;//為此對象實例設置唯一的lock_order。
      }
 }
 publiclonglockOrder(){
      returnlock_order;
 }
 public int[] array(){
     return arr;
 }
}
class SomeClass implements Runnable{
 public int sumArrays(ArrayWithLockOrdera1,ArrayWithLockOrdera2){
      intvalue=0;
      ArrayWithLockOrderfirst=a1;//保留數組引用的一個
     ArrayWithLockOrderlast=a2;//本地副本。
     intsize=a1.array().length;
      if(size==a2.array().length){
         if(a1.lockOrder()>a2.lockOrder()){//確定並設置對象的鎖定順序。
             first=a2;
            last=a1;
           }
         synchronized(first){//按正確的順序鎖定對象。
             synchronized(last){
                 int[]arr1=a1.array();
                 int[]arr2=a2.array();
                 for(int i=0;i<size;i++)
                       value+=arr1[i]+arr2[i];
               }
          }
     }
 return value;
 }
 public void run(){
 //
 }
}
對於4,如果線程進入,則線程在該類中所有操作不能進行,包括靜態變量靜態方法,實際上,對於含有靜態方法和靜態變量的代碼塊的同步,我們通常用4來加鎖.

synchronized理解

一、當兩個併發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
二、然而,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規則對其它對象鎖同樣適用。

synchronized用法

1.方法聲明時使用,放在範圍操作符(public等)後,其返回類型聲明(void等)之前。即一次只能有一個線程進入該方法,其他線程要想在此時調用該方法,只能排隊等候,當前線程(就是在synchronized方法內部的線程)執行完該方法後,別的線程才能進入。
例如:
public synchronized void synMethod() {
//方法體
}
2.對某一代碼塊使用,synchronized後跟括號,括號裏是變量,這樣,一次只有一個線程進入該代碼塊。例如:
public int synMethod(Object a1){
synchronized(Object) {
//一次只能有一個線程進入
}
}
3.synchronized後面括號裏是一對象,此時,線程獲得的是對象鎖。例如:
public class MyThread implements Runnable {
public static void main(String args[]) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt, "t1");
Thread t2 = new Thread(mt, "t2");
Thread t3 = new Thread(mt, "t3");
Thread t4 = new Thread(mt, "t4");
Thread t5 = new Thread(mt, "t5");
Thread t6 = new Thread(mt, "t6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
}
}
}
對於3,如果線程進入,則得到對象鎖,那麼別的線程在該類所有對象上的任何操作都不能進行。在對象級使用鎖通常是一種比較粗糙的方法。為什麼要將整個對象都上鎖,而不允許其他線程短暫地使用對象中其他同步方法來訪問共享資源?如果一個對象擁有多個資源,就不需要只為了讓一個線程使用其中一部分資源,就將所有線程都鎖在外面。由於每個對象都有鎖,可以如下所示使用虛擬對象來上鎖:
class FineGrainLock {
MyMemberClass x, y;
Object xlock = new Object(), ylock = new Object();
public void foo() {
synchronized(xlock) {
//access x here
}
//do something here - but don‘t use shared resources
synchronized(ylock) {
//access y here
}
}
public void bar() {
synchronized(this) {
//access both x and y here
}
//do something here - but don‘t use shared resources
}
}
4.synchronized後面括號裏是類。例如:
class ArrayWithLockOrder{
private static long num_locks = 0;
private long lock_order;
private int[] arr;
public ArrayWithLockOrder(int[] a)
{
arr = a;
synchronized(ArrayWithLockOrder.class) {//-----------------------------------------這裏
num_locks++; // 鎖數加 1。
lock_order = num_locks; // 為此對象實例設置唯一的 lock_order。
}
}
public long lockOrder()
{
return lock_order;
}
public int[] array()
{
return arr;
}
}
class SomeClass implements Runnable
{
public int sumArrays(ArrayWithLockOrder a1,
ArrayWithLockOrder a2)
{
int value = 0;
ArrayWithLockOrder first = a1; // 保留數組引用的一個
ArrayWithLockOrder last = a2; // 本地副本。
int size = a1.array().length;
if (size == a2.array().length)
{
if (a1.lockOrder() > a2.lockOrder()) // 確定並設置對象的鎖定
{ // 順序。
first = a2;
last = a1;
}
synchronized(first) { // 按正確的順序鎖定對象。
synchronized(last) {
int[] arr1 = a1.array();
int[] arr2 = a2.array();
for (int i=0;i<size;i++)value += arr1[i] + arr2[i];
}
}
}
return value;
}
public void run() {
//...
}
}
對於4,如果線程進入,則線程在該類中所有操作不能進行,包括靜態變量和靜態方法,實際上,對於含有靜態方法和靜態變量的代碼塊的同步,我們通常用4來加鎖。
以上4種之間的關係:
鎖是和對象相關聯的,每個對象有一把鎖,為了執行synchronized語句,線程必須能夠獲得synchronized語句中表達式指定的對象的鎖,一個對象只有一把鎖,被一個線程獲得之後它就不再擁有這把鎖,線程在執行完synchronized語句後,將獲得鎖交還給對象。
在方法前面加上synchronized修飾符即可以將一個方法聲明為同步化方法。同步化方法在執行之前獲得一個鎖。如果這是一個類方法,那麼獲得的鎖是和聲明方法的類相關的Class類對象的鎖。如果這是一個實例方法,那麼此鎖是this對象的鎖。synchronzied塊後面跟類的具體詳細例子:
public class DB2_JDBCFactory {
private static DB2_JDBCFactory instance = null;
public static final ThreadLocal threadLocal = new ThreadLocal();
private DB2_JDBCFactory() {
}
public static DB2_JDBCFactory getInstance() {
if(instance == null) {
synchronized(DB2_JDBCFactory.class) { //synchronized後面跟一個類
instance = new DB2_JDBCFactory();
}
}
return instance;
}
public Connection getConnection_JNDI_localhost(){
Connection c = (Connection) threadLocal.get();
try {
if (c == null || c.isClosed()) {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/localhost");
c = ds.getConnection();
threadLocal.set(c);
}
} catch (Exception ex) {
System.err.println("getConnection_JNDI Initial failed. " + ex);
return null;
}
return c;
}
} 外面的對象訪問這個類的 需要通過調用它的getInstance()