JAVA中ReentrantLock詳解


前言:本文解決的問題

  • RentrantLock與Synchronized區別
  • ReentrantLock特征
  • ReentrantLock類的方法介紹

1.什么是ReentrantLock

1.1ReentrantLock 與Synchronized區別

在面試中詢問ReentrantLock與Synchronized區別時,一般回答都是

ReentrantLock

  • ReentrantLock是JDK方法,需要手動聲明上鎖和釋放鎖,因此語法相對復雜些;如果忘記釋放鎖容易導致死鎖
  • ReentrantLock具有更好的細粒度,可以在ReentrantLock里面設置內部Condititon類,可以實現分組喚醒需要喚醒的線程
  • RenentrantLock能實現公平鎖

Synchronized

  • Synchoronized語法上簡潔方便
  • Synchoronized是JVM方法,由編輯器保證枷鎖和釋放

1.2ReentrantLock特征介紹

JAVA的java.util.concurrent框架中提供了ReentrantLock類(於JAVA SE 5.0時引入),ReentrantLock實現了lock接口,具體在JDK中的定義如下:

public class ReentrantLock implements Lock, java.io.Serializable {

 public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

}

看到一個類首先就需要知道它的構造方法有哪些,ReentrantLock有兩個構造方法,一個是無參的 ReentrantLock() ;另一個含有布爾參數public ReentrantLock(boolean fair)。后面一個構造函數說明ReentrantLock可以新建公平鎖;而Synchronized只能建立非公平鎖

那么Lock接口有哪些方法
lock
Lock接口中有lock和unlock方法,還有newCondition() 方法,這就是上面說的ReentrantLock里面設置內部Condititon類。由於ReentrantLock實現了Lock接口,因此它必須實現該方法,具體如下:

  public Condition newCondition() {
        return sync.newCondition();
    }

返回Condition類的一個實例。

2 ReentrantLock其它方法介紹

在介紹它的其它方法前,要先明白它的使用方法,以下JDK中的建議:

 class X {
     private final ReentrantLock lock = new ReentrantLock();
      // ...
  
      public void m() {
       lock.lock();  // block until condition holds
       try {
        // ... method body
        } finally {
        lock.unlock()
      }
    }

建議用try,在finally里面一定要釋放鎖,防止被中斷時鎖沒釋放,造成死鎖

lock()

 public void lock() {
        sync.lock();
    }

如果該鎖沒被其它線程獲得,則立即返回;並且把 lock hold count的值變位1.

unlock()

 public void unlock() {
        sync.release(1);
    }

如果當前線程是該鎖的持有者,則保持計數遞減。 如果保持計數現在為零,則鎖定被釋放。 如果當前線程不是該鎖的持有者,則拋出IllegalMonitorStateException 。

isFair()

public final boolean isFair() {
        return sync instanceof FairSync;
    }

判斷該鎖是不是公平鎖

newCondition()

 public Condition newCondition() {
        return sync.newCondition();
    }

返回新的ConditionObject對象。

Condition接口中的方法

  • await(): void await() throws InterruptedException;
    Condition接口中的方法,導致當前線程等到發信號。

  • siginal()

 /**
         * Moves the longest-waiting thread, if one exists, from the
         * wait queue for this condition to the wait queue for the
         * owning lock.
         *
         * @throws IllegalMonitorStateException if {@link #isHeldExclusively}
         *         returns {@code false}
         */
        public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }

喚醒一個等待該條件的線程去獲得鎖(第一個)。

  • signalAll():喚醒所有等待線程。

3 ReentrantLock完整實例介紹

package chapter10.reentrantlock;

import java.util.Arrays;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*模擬轉賬,把錢從一個賬戶轉到另一個賬戶
 * */
public class ReentrantLockUse {
	public static final int NACCOUNTS = 100;
	public static final double INITIAL_BALANCE = 1000;
	public static final double MAX_AMOUNT = 1000;
	public static final int DELAY = 10;

	
	public static void main(String[] args) {
		Bank bank = new Bank(NACCOUNTS,INITIAL_BALANCE);
		for(int i = 0 ; i < NACCOUNTS ; i++) {
			int fromAccount = i ;
			Runnable r = () ->{//lambda表達式
				try {
					while(true) {
						int toAccount  = (int) (bank.size()*Math.random());
						double amount = MAX_AMOUNT * Math.random();
						bank.transfer(fromAccount, toAccount, amount);
						Thread.sleep((int)(DELAY*Math.random()));
					}
				}
				catch(InterruptedException e) {
					
				}
			};
			Thread t = new Thread(r);//新建線程
			t.start();
		}

	}

}

class Bank{
	private final double[] account;//賬戶
	private Lock bankLock ;     //重復鎖
	private Condition sufficientFunds;	//條件對象
	
	
	
	public Bank(int n, double initialBalance) {
		account = new double[n];		
		Arrays.fill(account, initialBalance);
		bankLock = new ReentrantLock();  //構造對象時,實例化鎖
		sufficientFunds = bankLock.newCondition();//新建條件對象
	}
	/*轉賬,把from賬戶里面的錢轉到to里面,金額是amount*/
	public void transfer(int from , int to,double amount) {
		
		bankLock.lock();
		try {
			while(account[from] < amount) {  
				sufficientFunds.await();
			}
			System.out.println(Thread.currentThread());
			account[from] -=amount;
			System.out.printf("%10.2f from %d to %d ",amount,from,to);
			account[to] +=amount;
			System.out.printf(" Total Balance : %10.2f%n", getTotalBalance());
			sufficientFunds.signalAll();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally {
			bankLock.unlock();
		}
	}
	/*做的所有賬戶總額*/
	public double getTotalBalance() {
		bankLock.lock();
		try {
			 double sum = 0;
			 for(double a : account) {
				 sum +=a;
			 }
			 return sum;
		}
		finally {
			bankLock.unlock();
		}
	}
	
	public int size() {
		return account.length;
	}
}

執行結果
reentrantLOck執行結果

結果分析
循環建立100個線程,每個線程都在不停轉賬,由於ReentrantLock的使用,任何時刻所有賬戶的總額都保持不變。另外,把錢amount從A賬戶轉到B賬戶,要先判斷A賬戶中是否有這么多錢,不過沒有就調用條件對象ConditionObject中的await()方法,放棄該線程,等該其它線程轉錢進來;轉錢完成后調用.siginalAll()。


免責聲明!

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



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