我們前篇介紹了點關(guān)于線程創(chuàng)建的方式以及Thread相關(guān)api的介紹。這次我們說說線程中鎖的應(yīng)用。多線程中,數(shù)據(jù)同步是一個很讓人頭疼的事情,并且寫代碼中我們很容易寫出線程不安全的代碼,在查問題的時候也是特別不容易的查出來。java中在線程同步中采取了鎖的方式來讓數(shù)據(jù)同步。
synchronized 關(guān)鍵字 使用
- 把非線程安全的變成線程安全的。在方法名前面加上該關(guān)鍵字即可。
public synchronized String getContent(){
return null ;
}
雖然這兩種方法中都可以形成線程安全,但是如果在操作中不當,也會出現(xiàn)線程不安全的結(jié)果。我們在方法中有鎖,如果我們在該類上創(chuàng)建多個實例,那么鎖的作用就會消失。還會出現(xiàn)線程不安全的狀況。在項目中如果方法中加鎖,最好采用的是同一個類或者是靜態(tài)方法,這樣保證使用的是同一個鎖,不會是多個鎖。保證了線程安全的問題。方法上加鎖,如果在方法中有很多代碼其實不需要執(zhí)行同步操作,只是一部分需要同步操作,在整個方法上加鎖,就容易導(dǎo)致該方法區(qū)的內(nèi)容運行過慢,效率降低。
public static synchronized String getContent(){
return null ;
}
- 使用該關(guān)鍵字,形成同步代碼塊。
同步代碼塊可以幫助解決上面的問題。將一部分同步,一部分異步.這樣同步的地方就可以并行執(zhí)行,增大效率。
public String getContent(Object object){
Stirng result ;
synchronized (Object.class){
return null ;
}
return result;
}
- 同步代碼塊與在方法區(qū)上使用synchronized關(guān)鍵字的作用
- 在調(diào)用時,呈現(xiàn)阻塞的狀態(tài)。
- 同一時間內(nèi),只有一個線程可以調(diào)用代碼塊中的代碼
- 同步代碼塊還支持其他對象來實現(xiàn)同步的功能
- 代碼塊要求的是()中的對象監(jiān)視器,并且在同步代碼塊中我們使用的同一個對象,那么在代碼塊中的監(jiān)視器就可以是this或者其他的非this對象,但是對于類似Stirng或者Integer這種不變的對象不可用。無法鎖定一個對象,在多線程時刻容易出現(xiàn)線程安全問題。
volatile 關(guān)鍵字
- 使變量在多個線程之間可見。我們都是到在線程中會有私有堆棧和共有堆棧,那么volatile 的作用就是讓其在線程訪問變量時,強制性的從共有堆棧中取值。也就是我們平常所說的主內(nèi)存而不是線程工作內(nèi)容中讀取,保證了線程安全的可見性。但是只是保證可見性,不保證原子性。沒有同步特性。
Lock的使用
- ReentrantLock 介紹
ReentrantLock 可以達到我們使用synchronized 鎖達到的同步效果 ,并且還能擴展更強大。在使用中我們一般使用的是以下一些方法。并且該鎖是一個完全互斥拍他的鎖。讀寫不分離- lock(): 獲得鎖。
- unlock(): 釋放鎖。該方法使用一般都會放在finally中 ,如果線程出現(xiàn)異常退出,應(yīng)該把鎖也釋放掉,讓其他新城也獲得該鎖。程序能繼續(xù)執(zhí)行.
- Condition 實現(xiàn)等待/通知 。如果我們想實現(xiàn)單個對單個的通知,那么我們可以創(chuàng)建多個Condition來幫助我們實現(xiàn)一對一的方式。Condition中有await和signal 還有signalAll();await實現(xiàn)的是等待的方式,signal
實現(xiàn)的是通知等待的線程繼續(xù)執(zhí)行。我們在使用該等待方式之前必須,先獲得鎖才能使用,不然獲得就是異常.
- 公平鎖與非公平鎖:公平鎖是在先進來的線程會先獲得資源。非公平鎖先進來的線程不一定會先獲得鎖,可能有的線程一直拿著鎖不放,容易造成線程鎖之間的不公平。
- 簡單Api介紹
public void getContent(Integer value ){ try{ lock.lock(); lock.getHoldCount(); //查詢當前線程保持的此鎖定的個數(shù)。也就是調(diào)用lock()方法的次數(shù)。 lock.getQueueLength(); // 返回掙等待獲取此鎖定 的線程估計數(shù)。 lock.getWaitQueueLength(condition); //返回等待與此鎖定相關(guān)條件Condition估計數(shù)。 lock.hasQueuedThread(Thread threads) ; //作用查詢指定的線程是否在等待獲取此鎖定 lock.hasQueuedThreads(); //查詢 是否有線程正在等待獲取此鎖定。 lock.hasWaiters(condition); // 查詢是否有線程正在等待此鎖定有關(guān)的condition條件。 lock.isFair(); // 判斷是不是公平鎖 lock.isHeldByCurrentThread(); // 查詢當前線程是否保持此鎖定 lock.isLocked(); //作用是查詢此鎖定是否由任意線程保持。 lock.lockInterruptibly(); // 如果當前線程未被中斷,則獲取鎖定。如果已經(jīng)被中斷則出現(xiàn)異常 lock.tryLock(); //僅在 調(diào)用時鎖定未被另一個線程保持的情況下,才獲取該鎖定 System.out.println("打印內(nèi)容: " + Instant.now()); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } - ReentrantReadWriteLock 讀寫鎖
- 該鎖方便的在于讀寫鎖進行分離,我們在操作時讀鎖與讀鎖互不影響,那么就可以提高我們在程序中的效率。
- 讀鎖與讀鎖之間互不影響,能共同存在,不排斥。
- 讀鎖與寫鎖之間,讀鎖與寫鎖互斥,讀取數(shù)據(jù)時寫鎖不能執(zhí)行。一鎖執(zhí)行
- 寫鎖與寫鎖之間,也互斥,同時只能一個鎖在執(zhí)行。
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
readWriteLock.writeLock().lock();

TIM截圖20180405235154.jpg