方法
synchronized 方法
1.方法聲明時使用,放在範圍操作符(public等)之後,返回類型聲明(void等)之前.這時,執行緒獲得的是成員鎖,即一次只能有一個執行緒進入該方法,其他執行緒要想在此時調用該方法,只能排隊等候,當前執行緒(就是在synchronized方法內部的執行緒)執行完該方法後,別的執行緒才能進入.
例如:
如線上程t1中有語句obj.synMethod(); 那么由於synMethod被synchronized修飾,在執行該語句前, 需要先獲得調用者obj的對象鎖, 如果其他執行緒(如t2)已經鎖定了obj (可能是通過obj.synMethod,也可能是通過其他被synchronized修飾的方法obj.otherSynMethod鎖定的obj), t1需要等待直到其他執行緒(t2)釋放obj, 然後t1鎖定obj, 執行synMethod方法. 返回之前釋放obj鎖.
2.對某一代碼塊使用,synchronized後跟括弧,括弧里是變數,這樣,一次只有一個執行緒進入該代碼塊.此時,執行緒獲得的是成員鎖.例如:
3.synchronized後面括弧里是一對象,此時,執行緒獲得的是對象鎖.例如:
對於3,如果執行緒進入,則得到當前對象鎖,那么別的執行緒在該類所有對象上的任何操作都不能進行.在對象級使用鎖通常是一種比較粗糙的方法。為什麼要將整個對象都上鎖,而不允許其他執行緒短暫地使用對象中其他同步方法來訪問共享資源?如果一個對象擁有多個資源,就不需要只為了讓一個執行緒使用其中一部分資源,就將所有執行緒都鎖在外面。由於每個對象都有鎖,可以如下所示使用虛擬對象來上鎖:
4.synchronized後面括弧里是類,此時,執行緒獲得的是對象鎖.例如:
對於4,如果執行緒進入,則執行緒在該類中所有操作不能進行,包括靜態變數和靜態方法,實際上,對於含有靜態方法和靜態變數的代碼塊的同步,我們通常用4來加鎖.
理解
一、當兩個並發執行緒訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個執行緒得到執行。另一個執行緒必須等待當前執行緒執行完這個代碼塊以後才能執行該代碼塊。
二、然而,另一個執行緒仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關鍵的是,當一個執行緒訪問object的一個synchronized(this)同步代碼塊時,其他執行緒對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊,它就獲得了這個object的對象鎖。結果,其它執行緒對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規則對其它對象鎖同樣適用。
用法
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() |