日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)

發(fā)布時間:2023/11/28 生活经验 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

本文對Java的一些鎖的概念和實現(xiàn)做個整理,涉及:公平鎖和非公平鎖、可重入鎖(又名遞歸鎖)、自旋鎖、獨占鎖(寫)/共享鎖(讀)/互斥鎖、讀寫鎖

公平鎖和非公平鎖

概念

  • 公平鎖是指多個線程按照申請鎖的順序來獲取鎖。類似于進(jìn)程的FCFS(先來先服務(wù)),隊列的FIFO(先來先輸出)
  • 非公平鎖是指在多線程獲取鎖的順序并不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優(yōu)先獲取到鎖,在高并發(fā)的情況下,有可能造成優(yōu)先級反轉(zhuǎn)或者饑餓現(xiàn)象(長時間無法獲得鎖)

區(qū)別

公平鎖:公平鎖就很公平。在并發(fā)環(huán)境中,每個線程在獲取鎖時會先查看這個鎖維護(hù)的等待隊列,如果為空,即當(dāng)前線程是等待隊列中的第一個就占有鎖,否則就加入等待隊列中,根據(jù)FIFO的規(guī)則等待占有鎖。
非公平鎖:這個就很不公平了。在并發(fā)環(huán)境中,每個線程一上來不管三七二十一就嘗試搶占鎖,沒有搶到鎖再采用類似公平鎖的那種機(jī)制。

實現(xiàn)

  • sychronized就是一種非公平鎖。
  • ReentrantLock通過構(gòu)造函數(shù)指定該鎖是否是公平鎖(true為公平鎖,false為非公平鎖),默認(rèn)是非公平鎖(false),非公平鎖的優(yōu)點在于吞吐量比公平鎖大。

可重入鎖

概念

可重入鎖也叫遞歸鎖,指同一個線程在獲得了外層函數(shù)的鎖后,內(nèi)層遞歸函數(shù)仍然能獲取該鎖的代碼。也就是說,同一線程在外層方法獲得鎖后,在進(jìn)入內(nèi)層方法會自動獲取鎖。

作用

可重入鎖最大的作用就是避免死鎖

實現(xiàn)

ReentrantLock和synchronized都是典型的可重入鎖,給出ReentrantLock和synchronized同步鎖的代碼實現(xiàn):
1.使用sychronized關(guān)鍵字演示可重入鎖:

public class SychronizedDemo {public static void main(String[] args) {Phone phone = new Phone();new Thread(()->{phone.call();},"thread1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}phone.sendSMS();},"thread2").start();}
}class Phone{public synchronized void call(){System.out.println(Thread.currentThread().getName() + "\t 撥打電話");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}sendSMS();}public synchronized void sendSMS(){System.out.println(Thread.currentThread().getName() + "\t 發(fā)短信");}
}

代碼解讀:
這段代碼的輸出結(jié)果是多少呢?有人可能會認(rèn)為是:

thread1	 撥打電話
thread2	 發(fā)短信
thread1	 發(fā)短信

因為在線程1獲得了call方法的鎖輸出了“thread1撥打電話”之后睡眠了2秒,然后這時線程2肯定能夠槍到sendSMS方法的鎖輸出“thread2發(fā)短信”最后才是線程1醒了獲取到sendSMS方法的鎖輸出“thread1發(fā)短信”。
這種想法是沒錯,但不要忘了sychronized是可重入鎖,在線程1得到call方法的鎖后就已經(jīng)得到了其方法內(nèi)部的sendSMS方法的鎖,這時線程2去執(zhí)行sendSMS方法的時候會發(fā)現(xiàn)該方法是出于鎖住的狀態(tài)然后線程2阻塞,等到線程1執(zhí)行完call方法釋放sendSMS方法的鎖之后線程2才能繼續(xù)執(zhí)行。所以正確的輸出結(jié)果是:

thread1	 撥打電話
thread1	 發(fā)短信
thread2	 發(fā)短信

2.使用ReentrantLock演示可重入鎖:

public class ReentrantLockDemo {public static void main(String[] args) {Phone2 phone = new Phone2();new Thread(()->{phone.call();},"thread1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}phone.sendSMS();},"thread2").start();}
}class Phone2{Lock lock = new ReentrantLock();public void call(){try{lock.lock();System.out.println(Thread.currentThread().getName() + "\t 撥打電話");try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}sendSMS();}finally {lock.unlock();}}public void sendSMS(){try {lock.lock();System.out.println(Thread.currentThread().getName() + "\t 發(fā)短信");}finally {lock.unlock();}}
}

輸出結(jié)果和使用sychronized一樣。

自旋鎖

概念

自旋鎖是指嘗試獲取鎖的線程不會立即阻塞,而是采用循環(huán)的方式去獲取鎖,這樣的好處是減少線程上下文切換帶來的消耗,但是循環(huán)會帶來CPU的消耗。

實現(xiàn)

其實CAS的核心實現(xiàn)類UnSafe采用的就是自旋鎖:

 public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}

自寫自旋鎖實現(xiàn):

public class SpinLockDemo {private AtomicReference<Thread> atomicReference = new AtomicReference<>();public void myLock(){Thread thread = Thread.currentThread();System.out.println(thread.getName() + " come in");while (!atomicReference.compareAndSet(null,thread)){}System.out.println(thread.getName() + " 獲得鎖");}public void myUnLock(){Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread,null);System.out.println(thread.getName() + " myUnLock");}public static void main(String[] args) {SpinLockDemo spinLockDemo = new SpinLockDemo();new Thread(()->{spinLockDemo.myLock();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myUnLock();},"thread1").start();new Thread(()->{try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}spinLockDemo.myLock();spinLockDemo.myUnLock();},"thread2").start();}
}

輸出結(jié)果:

thread1 come in
thread1 獲得鎖
thread2 come in
thread1 myUnLock
thread2 獲得鎖
thread2 myUnLock

獨占鎖(寫)/共享鎖(讀)/互斥鎖

概念

獨占鎖:指該鎖只能被一個線程所持有,如ReentrantLock和sychronized都是獨占鎖
共享鎖:指該鎖可以被多個線程所持有,如ReentrantReadWriteLock其讀鎖是共享鎖,寫是獨占鎖。
讀的共享鎖可以保證并發(fā)讀是非常高效的。
互斥鎖:互斥鎖是一種簡單的加鎖的方法來控制對共享資源的訪問,互斥鎖只有兩種狀態(tài),即上鎖(lock)和解鎖(unlock)。如ReentrantLock和sychronized都是互斥鎖

實現(xiàn)

下面是使用ReentrantReadWriteLock實現(xiàn)讀時共享寫時獨占

public class ReadWriteLockDemo {public static void main(String[] args) {MyCaChe myCaChe = new MyCaChe();for (int i = 1; i <= 5; i++) {final int temp = i;new Thread(() -> {myCaChe.put(temp + "", temp);}, String.valueOf(i)).start();}for (int i = 1; i <= 5; i++) {int finalI = i;new Thread(() -> {myCaChe.get(finalI + "");}, String.valueOf(i)).start();}}
}class MyCaChe {private volatile Map<String, Object> map = new HashMap<>();private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();public void put(String key, Object value) {reentrantReadWriteLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + "\t正在寫入" + key);//模擬網(wǎng)絡(luò)延時try {TimeUnit.MICROSECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() + "\t完成寫入" + key);} finally {reentrantReadWriteLock.writeLock().unlock();}}public void get(String key) {reentrantReadWriteLock.readLock().lock();try {System.out.println(Thread.currentThread().getName() + "\t正在讀取" + key);//模擬網(wǎng)絡(luò)延時try {TimeUnit.MICROSECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object result = map.get(key);System.out.println(Thread.currentThread().getName() + "\t完成讀取" + key);} finally {reentrantReadWriteLock.readLock().unlock();}}public void clearCaChe() {map.clear();}
}

代碼

本文所涉及的所有代碼都在我的GitHub上:代碼

總結(jié)

以上是生活随笔為你收集整理的java锁(公平锁和非公平锁、可重入锁(又名递归锁)、自旋锁、独占锁(写)/共享锁(读)/互斥锁、读写锁)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。