Lock簡(jiǎn)單介紹
public class LockDemo {
// 聲明一個(gè)lock鎖
private Lock lock = new ReentrantLock();
private int count;
// 用lock來(lái)加鎖
public void increament01() {
// 加鎖
lock.lock();
try {
count++;
} finally {
// 釋放鎖
lock.unlock();
}
}
// syn關(guān)鍵字加鎖
public synchronized void increament02() {
count++;
}
}
釋放鎖的代碼應(yīng)當(dāng)放在finally關(guān)鍵字中,以保證代碼出現(xiàn)異常后,鎖能夠及時(shí)的釋放掉。
syn和lock的比較
- syn稱(chēng)之為內(nèi)置鎖,因?yàn)樗且粋€(gè)關(guān)鍵字,在jdk中沒(méi)有源碼,經(jīng)過(guò)多年優(yōu)化,性能很優(yōu)秀,并且代碼較為簡(jiǎn)潔,但較lock有局限性。
- lock稱(chēng)之為顯示鎖,在獲取鎖的過(guò)程中可以被中斷,并且還可以超時(shí)獲取鎖,和嘗試加鎖,syn則沒(méi)有這三個(gè)特點(diǎn)。
- syn和lock都是可重入鎖。
- ReentrantLock和syn關(guān)鍵字都是排他鎖,即在同一個(gè)時(shí)刻只允許一個(gè)線程訪問(wèn)。
總結(jié):如果沒(méi)有第二條中的業(yè)務(wù)場(chǎng)景的時(shí)候,應(yīng)該用syn關(guān)鍵字。
公平鎖和非公平鎖
如果在時(shí)間上,先對(duì)鎖進(jìn)行獲取的請(qǐng)求,一定先被滿足,這個(gè)鎖就是公平的,如果不滿足,這個(gè)鎖就不非公平的,一般來(lái)說(shuō),非公平鎖效率相對(duì)較高。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
源碼看出,lock默認(rèn)是非公平的鎖。
Lock和Condition
舉例說(shuō)明:
下面是一個(gè)快遞類(lèi):一個(gè)lock下面可以有多個(gè)condition,因?yàn)樾枰O(jiān)控兩個(gè)條件的變化,所以定義了兩個(gè)condition來(lái)鎖這兩個(gè)變量。lock鎖不會(huì)自動(dòng)釋放,需要手動(dòng)釋放。使線程進(jìn)入等待狀態(tài)的方法不是object的wait方法,而是condition接口中的await方法,喚醒線程的方法也是condition接口中的signal和signalAll,這點(diǎn)很重要。
public class Express {
// 聲明鎖
private Lock lock = new ReentrantLock();
// 公里變化鎖
private Condition kmCondition = lock.newCondition();
// 地點(diǎn)變化鎖
private Condition siteCondition = lock.newCondition();
// 始發(fā)地
private final static String CITY = "ShangHai";
// 里程變化
private int km;
// 地點(diǎn)變化
private String site;
Express() {
}
Express(int km, String site) {
this.km = km;
this.site = site;
}
// 里程數(shù)變化,會(huì)喚起線程
public void changeKm() {
lock.lock();
try {
this.km = 101;
// 喚醒
kmCondition.signal();
} finally {
lock.unlock();
}
}
// 地點(diǎn)變化會(huì)喚起線程
public void changeSite() {
lock.lock();
try {
this.site = "BeiJing";
// 喚醒
siteCondition.signal();
} finally {
lock.unlock();
}
}
// 用來(lái)監(jiān)聽(tīng)里程數(shù)的變化
public void waitKm() {
lock.lock();
try {
while (this.km <= 100) {
System.out.println(Thread.currentThread().getId() + "監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。");
kmCondition.await();
System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===里程變化===的線程被喚醒了。。。");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getId() + "釋放了鎖。");
}
System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===里程變化===的線程去做相應(yīng)的事了");
}
// 用來(lái)監(jiān)聽(tīng)地點(diǎn)的變化
public void waitSite() {
lock.lock();
try {
while (CITY.equals(this.site)) {
System.out.println(Thread.currentThread().getId() + "監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。");
siteCondition.await();
System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getId() + "釋放了鎖。");
}
System.out.println(Thread.currentThread().getId() + "-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了");
}
}
main方法測(cè)試類(lèi):
分別啟動(dòng)三個(gè)監(jiān)控地點(diǎn)變化的線程和三個(gè)監(jiān)控公里數(shù)變化的線程,并使它們都進(jìn)入等待狀態(tài),然后改變地點(diǎn)變化。調(diào)用await方法后,每個(gè)被阻塞的線程都會(huì)加到隊(duì)列末尾排隊(duì)。
public class Test {
// 初始化快遞
private static Express express = new Express(0, "ShangHai");
// 用來(lái)監(jiān)聽(tīng)里程數(shù)變化的線程
static class CheckKm implements Runnable {
public void run() {
express.waitKm();
}
}
// 用來(lái)監(jiān)聽(tīng)地點(diǎn)變化的線程
static class CheckSite implements Runnable {
public void run() {
express.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
// 啟動(dòng)三個(gè)線程去監(jiān)聽(tīng)里程數(shù)的變化
for (int i = 0; i <= 2; i++) {
new Thread(new CheckKm()).start();
}
// 啟動(dòng)三個(gè)線程去監(jiān)聽(tīng)地點(diǎn)的變化
for (int i = 0; i <= 2; i++) {
new Thread(new CheckSite()).start();
}
// 主線程睡眠一秒,異常信息拋出去
Thread.sleep(1000);
// 讓快遞的地點(diǎn)發(fā)生變化
express.changeSite();
}
}
- 首先使用的是用signal來(lái)喚醒線程:?jiǎn)拘言诖薒ock對(duì)象上等待的單個(gè)線程。
11監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
13監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
12監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
16監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
14釋放了鎖。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
signal是喚醒當(dāng)前阻塞隊(duì)列中的第一個(gè),而不是隨機(jī)喚醒。
- 使用signalAll來(lái)喚醒線程:?jiǎn)拘言诖薒ock對(duì)象上等待的所有線程。
11監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
13監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
12監(jiān)控【公里數(shù)】變化的線程即將進(jìn)入等待狀態(tài)。。。
14監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
16監(jiān)控【地點(diǎn)】變化的線程即將進(jìn)入等待狀態(tài)。。。
15-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
15釋放了鎖。
15-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
14釋放了鎖。
14-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
16-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程被喚醒了。。。
16釋放了鎖。
16-號(hào)監(jiān)聽(tīng)===地點(diǎn)變化===的線程去做相應(yīng)的事了
signalAll喚醒所有當(dāng)前隊(duì)列中的等待線程。