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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

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

前言

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

公平鎖和非公平鎖

概念

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

區別

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

實現

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

可重入鎖

概念

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

作用

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

實現

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

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 發短信");}
}

代碼解讀:
這段代碼的輸出結果是多少呢?有人可能會認為是:

thread1	 撥打電話
thread2	 發短信
thread1	 發短信

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

thread1	 撥打電話
thread1	 發短信
thread2	 發短信

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 發短信");}finally {lock.unlock();}}
}

輸出結果和使用sychronized一樣。

自旋鎖

概念

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

實現

其實CAS的核心實現類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;}

自寫自旋鎖實現:

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();}
}

輸出結果:

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

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

概念

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

實現

下面是使用ReentrantReadWriteLock實現讀時共享寫時獨占

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);//模擬網絡延時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);//模擬網絡延時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上:代碼

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。