文章同步更新在個(gè)人公眾號“梓莘”,歡迎大家關(guān)注钥星,相互交流控汉。
公平鎖和非公平鎖
公平鎖:是指多個(gè)線程按照申請鎖的順序來獲取鎖,也就是遵循先來后到
非公平鎖:是指多個(gè)線程獲取鎖的順序并不是安裝申請鎖的順序腥沽,有可能后申請鎖的線程優(yōu)先獲得鎖,在高并發(fā)環(huán)境下鸠蚪,有可能造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象今阳。非公平就是允許加塞
在并發(fā)包ReentrantLock的創(chuàng)建可以執(zhí)行構(gòu)造函數(shù)的boolean類型來得到公平鎖和非公平鎖师溅,默認(rèn)是非公平鎖。
區(qū)別:
公平鎖:Threads acquire a fair lock in the order in which they required it
公平鎖盾舌,就是很公平墓臭,在并發(fā)環(huán)境下,每個(gè)線程在獲取鎖時(shí)會先查看此鎖維護(hù)的等待隊(duì)列妖谴,如果為空窿锉,或者當(dāng)前線程是等待隊(duì)列的第一個(gè),就占有鎖膝舅,否則就會加入到等待隊(duì)列中嗡载,以后會安裝FIFO的規(guī)則從隊(duì)列充取到自己。
非公平鎖:a nonfair lock permits barging:threads requesting a lock can jump ahead of the qyeye of waiting threads if the lock happend to be available when it is requested.
非公平鎖比較野蠻仍稀,上來就直接嘗試占有鎖洼滚,如果嘗試失敗,就再采用類似公平鎖那種方式
對于Java ReentrantLock而言:
通過構(gòu)造函數(shù)指定該鎖是否是公平鎖琳轿,默認(rèn)是非公平鎖判沟,非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大耿芹。
對于synchronized而言崭篡,也是一種非公平鎖。
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入鎖
指的是同一線程外層函數(shù)獲得鎖之后吧秕,內(nèi)層遞歸函數(shù)仍然能獲得該鎖的代碼琉闪,在同一個(gè)線程在外層方法獲得鎖的時(shí)候,在進(jìn)入內(nèi)層方法會自動(dòng)獲取鎖砸彬。也就是說颠毙,線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊。
package com.zixin;
class Photo{
public synchronized void sendSms() throws Exception{
System.out.println(Thread.currentThread().getId()+" invoked sendSMS");
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getId()+" invoked sendEmail");
}
}
/**
* @ClassName ReenterLockDemo
* @Description 指的是同一線程外層函數(shù)獲得鎖之后砂碉,內(nèi)層遞歸函數(shù)仍然能獲得該鎖的代碼蛀蜜,在同一個(gè)線程在外層方法獲得鎖的時(shí)候,在進(jìn)入內(nèi)層方法會自動(dòng)獲取鎖增蹭。也就是鎖滴某,線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊。
* @Author zishen
* @Date 2019/12/30 9:36
* @Version 1.0
**/
public class ReenterLockDemo {
/**
* 11 invoked sendSMS
* 11 invoked sendEmail
* 10 invoked sendSMS
* 10 invoked sendEmail
* @param args
*/
public static void main(String[] args) {
Photo p = new Photo();
new Thread(()->{
try {
p.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
try {
p.sendSms();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();
}
}
自旋鎖
嘗試獲取鎖的線程不會立即阻塞滋迈,而是采用循環(huán)的方式去嘗試獲取鎖霎奢,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會消耗CPU饼灿。
package com.zixin;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ClassName SpinLockDemo
* @Description 手寫一個(gè)自旋鎖
* @Author zixin
* @Date 2019/12/30 10:34
* @Version 1.0
**/
public class SpinLockDemo {
//原子引用線程
AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+" come in");
while(!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnLock(){
Thread thread =Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+" invoked myUnLock");
}
/**
* AA come in
* BB come in
* AA invoked myUnLock
* BB invoked myUnLock
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(()->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
spinLockDemo.myUnLock();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AA").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinLockDemo.myLock();
spinLockDemo.myUnLock();
},"BB").start();
}
}
獨(dú)占鎖/共享鎖
獨(dú)占鎖:指該鎖一次只能被一個(gè)線程所持有幕侠,對ReentrantLock和Synchronized而言都是獨(dú)占鎖、
共享鎖:指該鎖可被多個(gè)線程多持有碍彭。
對ReentrantReadWriteLock其讀鎖是共享鎖晤硕,其寫鎖是獨(dú)占鎖悼潭。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫窗骑,寫讀女责,寫寫的過程是互斥的。
package com.zixin;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
public void put(String key,Object value){
rwlock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 正在寫入:"+key);
TimeUnit.MILLISECONDS.sleep(500);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+" 寫入完成:"+key);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rwlock.writeLock().unlock();
}
}
public void get(String key){
rwlock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+" 正在讀却匆搿:"+key);
TimeUnit.MILLISECONDS.sleep(500);
Object result = map.get(key);
System.out.println(Thread.currentThread().getName()+" 讀取完成:"+result);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rwlock.readLock().unlock();
}
}
public void clearMap(){
map.clear();
}
}
/**
* @ClassName ReadWriteLockDemo
* @Description 讀寫鎖
* @Author zixin
* @Date 2019/12/30 11:38
* @Version 1.0
**/
public class ReadWriteLockDemo {
/**
* 0 正在寫入:0
* 0 寫入完成:0
* 1 正在寫入:1
* 1 寫入完成:1
* 2 正在寫入:2
* 2 寫入完成:2
* 3 正在寫入:3
* 3 寫入完成:3
* 4 正在寫入:4
* 4 寫入完成:4
* 0 正在讀鹊种:0
* 1 正在讀取:1
* 2 正在讀热碜濉:2
* 3 正在讀人⑾病:3
* 4 正在讀取:4
* 1 讀取完成:1
* 2 讀取完成:2
* 0 讀取完成:0
* 3 讀取完成:3
* 4 讀取完成:4
* @param args
*/
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i <5 ; i++) {
final int tempInt = i;
new Thread(()->{
myCache.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}
for (int i = 0; i <5 ; i++) {
final int tempInt = i;
new Thread(()->{
myCache.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}