公平鎖和非公平鎖是什么弥奸?有什么區(qū)別?
- 并發(fā)包中ReentrantLock的創(chuàng)建可以指定構(gòu)造函數(shù)的布爾類型來(lái)得到公平鎖和非公平鎖奋早,默認(rèn)是非公平鎖盛霎。
ReentrantLock lock = new ReentrantLock(true);
- 兩者的區(qū)別
- 公平鎖:就是很公平赠橙,在并發(fā)環(huán)境中,每個(gè)線程在獲取鎖時(shí)會(huì)查看此鎖維護(hù)的等待隊(duì)列愤炸,如果為空期揪,或者當(dāng)前線程時(shí)等待隊(duì)列的第一個(gè),就占有鎖摇幻。否則就會(huì)加入到等待隊(duì)列中,以后會(huì)按照FIFO規(guī)則在隊(duì)列中取到自己挥萌。
- 非公平鎖:上來(lái)就直接占有鎖绰姻,如果嘗試失敗,就再采用類似公平鎖的那種方式引瀑。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大狂芋。
Synchronized也是一種非公平鎖。
可重入鎖(也叫遞歸鎖)
指同一線程在外層方法獲取鎖的時(shí)候憨栽,在內(nèi)層方法會(huì)自動(dòng)獲取鎖帜矾。
ReentrantLock和Synchronized是典型的可重入鎖
可重入鎖最大的作用是避免死鎖。
自旋鎖(spinLock)
是指嘗試獲取鎖的線程不會(huì)阻塞屑柔,而是采用循環(huán)的方式獲取鎖
- 好處:減少上下文切換的消耗
- 壞處:循環(huán)會(huì)消耗CPU
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
}
- 手寫自旋鎖
public class SpinLockDemo {
//原子引用線程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t comeIn");
while (!atomicReference.compareAndSet(null, thread)) {
}
}
public void myUnLock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t myUnlock ");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
}, "t1").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
spinLockDemo.myLock();
spinLockDemo.myUnLock();
}, "t2").start();
}
}
獨(dú)占鎖(寫鎖)屡萤,共享鎖(讀鎖),互斥鎖
- 獨(dú)占鎖是指鎖一次只能被一個(gè)線程所持有掸宛,對(duì)ReentrantLock和Synchronized而言死陆,都是獨(dú)占鎖。
- 共享鎖是指該鎖可能被多個(gè)線程所持有唧瘾。
對(duì)ReentrantWriteReadLock其讀鎖是共享鎖措译,其寫鎖是獨(dú)占鎖,寫寫饰序,讀寫领虹,寫讀過(guò)程都是互斥的。也就是多線程下讀的時(shí)候不寫求豫,寫的時(shí)候不讀塌衰,讀的時(shí)候其他線程可以讀。
- 讀寫鎖案例蝠嘉,手寫一個(gè)緩存
class myCache {//資源類
private volatile Map<String, Object> map = new HashMap<>();
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 正在寫入:key:" + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 寫入完成");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
public void get(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\t 開始讀猾蒂,key:" + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 讀完成,result:" + result);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
}
}
/**
* @author liujian
* @descripts 讀寫鎖
* @create 2019-06-22 17:26
* <p>
* 寫操作:獨(dú)占+原子,中間的過(guò)程必須是完整的統(tǒng)一體是晨,不允許被打斷肚菠,被分割
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
myCache myCache = new myCache();
for (int i = 0; i < 50; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.put(String.valueOf(tempInt), String.valueOf(tempInt));
}, String.valueOf(i)).start();
}
for (int i = 0; i < 50; i++) {
final int tempInt = i;
new Thread(() -> {
myCache.get(String.valueOf(tempInt));
}, String.valueOf(i)).start();
}
}
}
Synchronized和Lock有什么區(qū)別?用新的Lock有什么好處罩缴?舉例說(shuō)明
1. 原始構(gòu)成
Synchronized是關(guān)鍵字蚊逢,屬于JVM層面层扶。
- monitorenter 底層通過(guò)monitor對(duì)象完成,其實(shí)wait/notify等方法也依賴monitor對(duì)象烙荷,只有在代碼塊和方法中才可以使用wait/notify等方法镜会。
- monitorexit
Lock是具體的類(java.concurrent.locks.lock),是API層面的鎖终抽。
2. 使用方法
- Synchronized不需要用戶手動(dòng)去釋放鎖戳表,當(dāng)Synchronized代碼執(zhí)行完后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用昼伴。
- ReentrantLock 需要用戶手動(dòng)去釋放鎖匾旭,如果不釋放,會(huì)造成死鎖的現(xiàn)象圃郊。
3. 等待是否可中斷
synchronized不可中斷价涝,只有拋出異常和運(yùn)行完成兩種情況。
ReentrantLock 可中斷
- 設(shè)置超時(shí)方法中斷:tryLock(Long timeout ,TimeUnit unit)
- 在代碼塊中聲明方法:lock.lockInterruptibly(); 當(dāng)線程調(diào)用Thread.interrupted()時(shí)持舆,等待鎖的過(guò)程中會(huì)立即響應(yīng)中斷色瘩。
4. 加鎖是否公平
Synchronized 是非公平鎖
ReentrantLock 默認(rèn)非公平鎖,構(gòu)建函數(shù)傳入boolean值逸寓,true為公平鎖居兆,false為非公平鎖。
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
5. 鎖綁定多個(gè)條件
Synchronized沒(méi)有
ReentrantLock用來(lái)分組喚醒需要喚醒的線程們竹伸,可以精確喚醒史辙,而不是像synchronized要么喚醒一個(gè)線程要么喚醒全部線程。
demo演示
題目:多線程之間按順序調(diào)用佩伤,實(shí)現(xiàn)A->B->C三個(gè)線程啟動(dòng)聊倔,要求如下:
AA打印五次,BB打印十次生巡,CC打印15次
緊接著 AA打印五次耙蔑,BB打印十次,CC打印15次
...
來(lái)10輪
public class SynAndReentrantLockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
for (int i = 0; i <10 ; i++) {
new Thread(() -> shareResource.print(5), "AA").start();
new Thread(() -> shareResource.print(10), "BB").start();
new Thread(() -> shareResource.print(15), "CC").start();
}
}
}
class ShareResource {
private int num = 1;
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print(int j) {
lock.lock();
try {
if (j==5){
while (num != 1) {
c1.await();
}
for (int i = 0; i < j; i++) {
System.out.println(Thread.currentThread().getName() + "\t ");
}
num = 2;
c2.signal();
}else if (j==10){
while (num != 2) {
c2.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t ");
}
c3.signal();
num = 3;
}else if (j==15){
while (num != 3) {
c3.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t ");
}
c1.signal();
num = 1;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}