文章目錄
-
-
- 可重入鎖
- 可重入自旋鎖
- 可中斷鎖
- 公平鎖
- 讀寫鎖
-
可重入鎖
可重入鎖,指的是同一線程 外層方法獲得
鎖
之後 調用了仍然需要擷取
同一個鎖
的方法,不用重新擷取
鎖
,可以直接執行。在JAVA中synchronized和ReentrantLock 都是 可重入鎖。
可重入鎖最大的作用是避免死鎖
代碼示例1:
public class MyRunnable implements Runnable{
public synchronized void fun1(){
System.out.println(Thread.currentThread().getName());
fun2();
}
public synchronized void fun2(){
System.out.println(Thread.currentThread().getName());
}
@Override
public void run() {
fun1();
}
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
new Thread(mr).start();
new Thread(mr).start();
new Thread(mr).start();
}
}
代碼示例2:
ublic class MyRunnable implements Runnable {
ReentrantLock lock = new ReentrantLock();
public void fun1() {
lock.lock();
System.out.println(Thread.currentThread().getName());
fun2();
lock.unlock();
}
public void fun2() {
lock.lock();
System.out.println(Thread.currentThread().getName());
lock.unlock();
}
@Override
public void run() {
fun1();
}
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
new Thread(mr).start();
new Thread(mr).start();
new Thread(mr).start();
}
}
可重入自旋鎖
在擷取不到鎖的情況下,會讓目前線程一直做空循環,避免進入阻塞狀态
public class SpinLock {
private AtomicReference<Thread> owner =new AtomicReference<>();
private int count =0;
// 擷取鎖
public void lock(){
Thread current = Thread.currentThread();
// 為了避免不可重入,在lock的時候需要判斷操作的是否是同一線程
// 同一線程,多次調用lock,計數 +
if(current==owner.get()) {
count++;
return ;
}
// CAS不成功,空循環
while(!owner.compareAndSet(null, current)){
}
// 執行到這裡,就表示擷取到鎖了
}
// 釋放鎖
public void unlock (){
Thread current = Thread.currentThread();
if(current==owner.get()){
// 同一線程,多次調用unlock,計數歸零後,才能将鎖釋放掉
if(count!=0){
count--;
}else{
owner.compareAndSet(current, null);
}
}
}
}
可中斷鎖
如果某一線程A正在執行鎖中的代碼,另一線程B正在等待擷取該鎖,線程B不想等待,想先處理其他事情,我們可以讓它中斷自己或者在别的線程中中斷它,這種就是可中斷鎖。
在Java中,synchronized不是可中斷鎖,而Lock是可中斷鎖。
Lock#
lockInterruptibly()
與Lock#
lock()
方法(待擷取鎖成功後,才響應中斷。)不同,
優先考慮響應中斷,再去擷取鎖。
/**
* 擷取鎖,除非線程被打斷。
* 如果鎖不被其他線程持有,則擷取鎖,并立即傳回,将鎖持有計數設定為1。
* 如果目前線程已經持有此鎖,那麼持有計數将增加1,方法立即傳回。
* 如果鎖被另一個線程持有,那麼目前線程将出于線程排程的目的被禁用,
* 并處于休眠狀态,直到發生以下兩種情況之一:
* 1.鎖由目前線程擷取。
* 2.其他線程打斷目前線程。
* 如果鎖被目前線程擷取,那麼鎖持有計數被設定為1。
*
* 如果目前線程:進入此方法時,其中斷狀态是否已設定
* 或者當擷取鎖時被打斷
* 則會抛出InterruptedException,并清除interrupted status。
*
* 在這個實作中,由于這個方法是一個顯式的中斷點,
* 是以優先響應中斷而不是正常的或可重入的鎖擷取。
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 以獨占模式擷取,如果中斷将中止。
* 首先檢查中斷狀态,然後至少調用一次{@link #tryAcquire},成功後傳回。
* 否則,線程将排隊,可能反複阻塞和解除阻塞,調用{@link #tryAcquire},直到成功或線程被中斷。
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
公平鎖
盡量按照請求順序來擷取鎖。比如同時有多個線程在等待一個鎖,當這個鎖被釋放時,等待時間最久的線程(最先請求的線程)會獲得該所,這種就是公平鎖。非公平鎖即無法保證鎖的擷取是按照請求鎖的順序進行的。這樣就可能導緻某個或者一些線程永遠擷取不到鎖。在Java中,synchronized就是非公平鎖,它無法保證等待的線程擷取鎖的順序。而對于ReentrantLock和ReentrantReadWriteLock,它預設情況下是非公平鎖,但是可以設定為公平鎖。
/**
* 如果參數為true表示為公平鎖,為fasle為非公平鎖。
* 預設情況下,如果使用無參構造器,則是非公平鎖。
**/
ReentrantLock lock = new ReentrantLock(true);
讀寫鎖
對一個資源(比如檔案)的通路分成了2個鎖,一個讀鎖和一個寫鎖。正因為有了讀寫鎖,才使得多個線程之間的讀操作不會發生沖突。ReadWriteLock就是讀寫鎖,它是一個接口,ReentrantReadWriteLock實作了這個接口。可以通過readLock()擷取讀鎖,通過writeLock()擷取寫鎖。
更多鎖的隻是