可重入鎖 & 自旋鎖 & Java里的AtomicReference和CAS操作 & Linux mutex不可重入


之前還是寫過蠻多的關於鎖的文章的:

http://www.cnblogs.com/charlesblc/p/5994162.html 《【轉載】Java中的鎖機制 synchronized & 偏向鎖 & 輕量級鎖 & 重量級鎖 & 各自

http://www.cnblogs.com/charlesblc/p/5935326.html 《[Todo] 樂觀悲觀鎖,自旋互斥鎖等等

http://www.cnblogs.com/charlesblc/p/6146917.html 《【轉載】同步和互斥的POSIX支持(互斥鎖,條件變量,自旋鎖)

http://www.cnblogs.com/charlesblc/p/6134658.html 《【Todo】秒殺系統 & 樂觀鎖 & Nginx反向代理

http://www.cnblogs.com/charlesblc/p/5996255.html 《【Todo】【轉載】Java中的鎖機制2 - Lock

 

再寫一個,加深理解吧。

 

在學習Java內存模型的時候,看到ReentrantLock的描述,有點記不清了,就查了一下資料。

參考如下這篇文章:http://www.jb51.net/article/57338.htm

鎖作為並發共享數據,保證一致性的工具,在JAVA平台有多種實現(如 synchronized 和 ReentrantLock等等 ) 。這些已經寫好提供的鎖為我們開發提供了便利,但是鎖的具體性質以及類型卻很少被提及。本系列文章將分析JAVA下常見的鎖名稱以及特性,為大家答疑解惑。

 

可重入鎖:

本文里面講的是廣義上的可重入鎖,而不是單指JAVA下的ReentrantLock。

可重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之后 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。
在JAVA環境下 ReentrantLock 和synchronized 都是 可重入鎖。

使用實例:

public class Test implements Runnable{
 public synchronized void get(){
  System.out.println(Thread.currentThread().getId());
  set();
 }
 public synchronized void set(){
  System.out.println(Thread.currentThread().getId());
 }
 @Override
 public void run() {
  get();
 }
 public static void main(String[] args) {
  Test ss=new Test();
  new Thread(ss).start();
  new Thread(ss).start();
  new Thread(ss).start();
 }
}

結果如下,是正確的,即同一個線程id被連續輸出兩次。

Threadid: 8
Threadid: 8
Threadid: 10
Threadid: 10
Threadid: 9
Threadid: 9

 

可重入鎖最大的作用是避免死鎖。
我們以自旋鎖作為例子。(注:自旋鎖,就是拿不到鎖的情況會不停自旋循環檢測來等待,不進入內核態沉睡,而是在用戶態自旋嘗試)

public class SpinLock {
 private AtomicReference<Thread> owner =new AtomicReference<>();
 public void lock(){
  Thread current = Thread.currentThread();
  while(!owner.compareAndSet(null, current)){
  }
 }
public void unlock (){ Thread current = Thread.currentThread(); owner.compareAndSet(current, null); } }

上面是自旋鎖的一種實現。

對於自旋鎖來說:

1、若有同一線程兩調用lock() ,會導致第二次調用lock位置進行自旋,產生了死鎖
說明這個鎖並不是可重入的。(在lock函數內,應驗證線程是否為已經獲得鎖的線程)
2、若1問題已經解決,當unlock()第一次調用時,就已經將鎖釋放了。實際上不應釋放鎖。
(采用計數次進行統計)

 

修改之后,如下:

public class SpinLock1 {
 private AtomicReference<Thread> owner =new AtomicReference<>();
 private int count =0;
 public void lock(){
  Thread current = Thread.currentThread();
  if(current==owner.get()) {
   count++;
   return ;
  }
  while(!owner.compareAndSet(null, current)){
  }
 }
public void unlock (){ Thread current = Thread.currentThread(); if(current==owner.get()){ if(count!=0){ count--; }else{ owner.compareAndSet(current, null); } } } }

這種方式實現的自旋鎖即為可重入鎖。

 

另,看一下mutex的情況:

Mutex可以分為遞歸鎖(recursive mutex)和非遞歸鎖(non-recursive mutex)。可遞歸鎖也可稱為可重入鎖(reentrant mutex),
非遞歸鎖又叫不可重入鎖(non-reentrant mutex)。

二者唯一的區別是,同一個線程可以多次獲取同一個遞歸鎖,不會產生死鎖。而如果一個線程多次獲取同一個非遞歸鎖,則會產生死鎖。

Windows下的Mutex和Critical Section是可遞歸的。

Linux下的pthread_mutex_t鎖默認是非遞歸的。可以顯示的設置PTHREAD_MUTEX_RECURSIVE屬性,將pthread_mutex_t設為遞歸鎖。

 

(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM