天天看點

java 可重入鎖、自旋鎖、可中斷鎖、公平鎖、 讀寫鎖

文章目錄

      • 可重入鎖
      • 可重入自旋鎖
      • 可中斷鎖
      • 公平鎖
      • 讀寫鎖

可重入鎖

可重入鎖,指的是同一線程 外層方法獲得

之後 調用了仍然需要擷取

同一個鎖

的方法,不用重新擷取

,可以直接執行。在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()擷取寫鎖。

更多鎖的隻是

繼續閱讀