synchronized
關(guān)鍵字,,代表這個(gè)方法加鎖,相當(dāng)于不管哪一個(gè)線程(例如線程A),,運(yùn)行到這個(gè)方法時(shí),都要檢查有沒有其它線程B(或者C、 D等)正在用這個(gè)方法,,有的話要等正在使用synchronized方法的線程B(或者C ,、D)運(yùn)行完這個(gè)方法后再運(yùn)行此線程A,沒有的話,直接運(yùn)行。它包括兩種用法:synchronized 方法和 synchronized 塊,。
1. synchronized 方法:
聲明是為了定義變量的作用范圍和作用域
通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法,。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對(duì)類成員變量的訪問:每個(gè)類實(shí)例對(duì)應(yīng)一把鎖,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,,否則所屬線程阻塞,,方法一旦執(zhí)行,,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,,此后被阻塞的線程方能獲得該鎖,,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時(shí)刻對(duì)于每一個(gè)類實(shí)例,,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€(gè)能夠獲得該類實(shí)例對(duì)應(yīng)的鎖),,從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,,不光是類實(shí)例,,每一個(gè)類也對(duì)應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為 synchronized ,,以控制其對(duì)類的
靜態(tài)成員變量的訪問,。
synchronized 方法的缺陷:若將一個(gè)大的方法聲明為synchronized 將會(huì)大大影響效率,典型地,,若將線程類的方法 run() 聲明為 synchronized ,,由于在線程的整個(gè)生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對(duì)本類任何 synchronized 方法的調(diào)用都永遠(yuǎn)不會(huì)成功,。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中,,將其聲明為 synchronized ,并在主方法中調(diào)用來解決這一問題,,但是 Java 為我們提供了更好的解決辦法,,那就是 synchronized 塊。
2. synchronized 塊:
通過 synchronized關(guān)鍵字來聲明synchronized 塊,。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個(gè)代碼塊,,其中的代碼必須獲得對(duì)象 syncObject (如前所述,可以是類實(shí)例或類)的鎖方能執(zhí)行,,具體機(jī)制同前所述,。由于可以針對(duì)任意代碼塊,且可任意指定上鎖的對(duì)象,,故靈活性較高,。
對(duì)synchronized(this)的一些理解
一、當(dāng)兩個(gè)并發(fā)線程訪問同一個(gè)對(duì)象object中的這個(gè)synchronized(this)同步代碼塊時(shí),,一個(gè)時(shí)間內(nèi)只能有一個(gè)線程得到執(zhí)行,。另一個(gè)線程必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊。
二,、當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),,其他線程對(duì)object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
三,、然而,,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),,另一個(gè)線程仍然可以訪問該object中的除synchronized(this)同步代碼塊以外的部分。
四,、第三個(gè)例子同樣適用其它同步代碼塊,。也就是說,當(dāng)一個(gè)線程訪問object的一個(gè)synchronized(this)同步代碼塊時(shí),,它就獲得了這個(gè)object的
對(duì)象鎖,。結(jié)果,其它線程對(duì)該object對(duì)象所有同步代碼部分的訪問都被暫時(shí)阻塞,。
五,、以上規(guī)則對(duì)其它對(duì)象鎖同樣適用
synchronized的4種用法
1.方法聲明時(shí)使用,放在范圍操作符(public等)之后,,返回類型聲明(void等)之前,。即一次只能有一個(gè)線程進(jìn)入該方法,,其他線程要想在此時(shí)調(diào)用該方法,,只能排隊(duì)等候,當(dāng)前線程(就是在synchronized方法內(nèi)部的線程)執(zhí)行完該方法后,,別的線程才能進(jìn)入,。 例如: public synchronized void synMethod() { //方法體 } 2.對(duì)某一代碼塊使用,synchronized后跟括號(hào),,括號(hào)里是變量,,這樣,一次只有一個(gè)線程進(jìn)入該代碼塊,。例如: public int synMethod(int a1){ synchronized(a1) { //一次只能有一個(gè)線程進(jìn)入 } } 3.synchronized后面括號(hào)里是一對(duì)象,,此時(shí),線程獲得的是對(duì)象鎖,。例如: 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()); } } } 對(duì)于3,如果線程進(jìn)入,,則得到對(duì)象鎖,那么別的線程在該類所有對(duì)象上的任何操作都不能進(jìn)行,。在對(duì)象級(jí)使用鎖通常是一種比較粗糙的方法,。為什么要將整個(gè)對(duì)象都上鎖,而不允許其他線程短暫地使用對(duì)象中其他同步方法來訪問共享資源,?如果一個(gè)對(duì)象擁有多個(gè)資源,,就不需要只為了讓一個(gè)線程使用其中一部分資源,就將所有線程都鎖在外面,。由于每個(gè)對(duì)象都有鎖,,可以如下所示使用虛擬對(duì)象來上鎖: 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后面括號(hào)里是類。例如: 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++; // 鎖數(shù)加 1,。 lock_order = num_locks; // 為此對(duì)象實(shí)例設(shè)置唯一的 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; // 保留數(shù)組引用的一個(gè) ArrayWithLockOrder last = a2; // 本地副本,。 int size = a1.array().length; if (size == a2.array().length) { if (a1.lockOrder() > a2.lockOrder()) // 確定并設(shè)置對(duì)象的鎖定 { // 順序。 first = a2; last = a1; } synchronized(first) { // 按正確的順序鎖定對(duì)象,。 synchronized(last) { int[] arr1 = a1.array(); int[] arr2 = a2.array(); for (int i=0; i value += arr1[i] + arr2[i]; } } } return value; } public void run() { //... } } 對(duì)于4,如果線程進(jìn)入,,則線程在該類中所有操作不能進(jìn)行,包括靜態(tài)變量和靜態(tài)方法,,實(shí)際上,,對(duì)于含有靜態(tài)方法和靜態(tài)變量的代碼塊的同步,我們通常用4來加鎖,。 以上4種之間的關(guān)系: 鎖是和對(duì)象相關(guān)聯(lián)的,,每個(gè)對(duì)象有一把鎖,為了執(zhí)行synchronized語句,,線程必須能夠獲得synchronized語句中表達(dá)式指定的對(duì)象的鎖,,一個(gè)對(duì)象只有一把鎖,被一個(gè)線程獲得之后它就不再擁有這把鎖,,線程在執(zhí)行完synchronized語句后,,將獲得鎖交還給對(duì)象。 在方法前面加上synchronized修飾符即可以將一個(gè)方法聲明為同步化方法,。同步化方法在執(zhí)行之前獲得一個(gè)鎖,。如果這是一個(gè)類方法,那么獲得的鎖是和聲明方法的類相關(guān)的Class類對(duì)象的鎖,。如果這是一個(gè)實(shí)例方法,,那么此鎖是this對(duì)象的鎖。synchronzied塊后面跟類的具體詳細(xì)例子,、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后面跟一個(gè)類 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; }}外面的對(duì)象訪問這個(gè)類的 需要通過調(diào)用它的getInstance() |