文章同步更新在個(gè)人公眾號(hào)“梓莘”,歡迎大家關(guān)注,相互交流。
公平鎖和非公平鎖
公平鎖:是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖,也就是遵循先來(lái)后到
非公平鎖:是指多個(gè)線程獲取鎖的順序并不是安裝申請(qǐng)鎖的順序,有可能后申請(qǐng)鎖的線程優(yōu)先獲得鎖,在高并發(fā)環(huán)境下,有可能造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。非公平就是允許加塞
在并發(fā)包ReentrantLock的創(chuàng)建可以執(zhí)行構(gòu)造函數(shù)的boolean類(lèi)型來(lái)得到公平鎖和非公平鎖,默認(rèn)是非公平鎖。
區(qū)別:
公平鎖:Threads acquire a fair lock in the order in which they required it
公平鎖,就是很公平,在并發(fā)環(huán)境下,每個(gè)線程在獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列,如果為空,或者當(dāng)前線程是等待隊(duì)列的第一個(gè),就占有鎖,否則就會(huì)加入到等待隊(duì)列中,以后會(huì)安裝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.
非公平鎖比較野蠻,上來(lái)就直接嘗試占有鎖,如果嘗試失敗,就再采用類(lèi)似公平鎖那種方式
對(duì)于Java ReentrantLock而言:
通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖,非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大。
對(duì)于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)層方法會(huì)自動(dòng)獲取鎖。也就是說(shuō),線程可以進(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)層方法會(huì)自動(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ì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。
package com.zixin;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ClassName SpinLockDemo
* @Description 手寫(xiě)一個(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è)線程所持有,對(duì)ReentrantLock和Synchronized而言都是獨(dú)占鎖、
共享鎖:指該鎖可被多個(gè)線程多持有。
對(duì)ReentrantReadWriteLock其讀鎖是共享鎖,其寫(xiě)鎖是獨(dú)占鎖。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫(xiě),寫(xiě)讀,寫(xiě)寫(xiě)的過(guò)程是互斥的。
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()+" 正在寫(xiě)入:"+key);
TimeUnit.MILLISECONDS.sleep(500);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+" 寫(xiě)入完成:"+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 讀寫(xiě)鎖
* @Author zixin
* @Date 2019/12/30 11:38
* @Version 1.0
**/
public class ReadWriteLockDemo {
/**
* 0 正在寫(xiě)入:0
* 0 寫(xiě)入完成:0
* 1 正在寫(xiě)入:1
* 1 寫(xiě)入完成:1
* 2 正在寫(xiě)入:2
* 2 寫(xiě)入完成:2
* 3 正在寫(xiě)入:3
* 3 寫(xiě)入完成:3
* 4 正在寫(xiě)入:4
* 4 寫(xiě)入完成:4
* 0 正在讀?。?
* 1 正在讀?。?
* 2 正在讀?。?
* 3 正在讀取:3
* 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();
}
}
}