之前還是寫過蠻多的關於鎖的文章的:
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設為遞歸鎖。
(完)