重入鎖,ReentrantLock 和 Condition 的使用

重入鎖 ReentrantLock 主要特點:

  1. 容易控制加鎖的粒度。lock unlock
  2. 等待鎖的過程當中可以響應中斷,lockInterruptibly
  3. 可以有限時間內嘗試獲取鎖, tryLock
  4. 公平鎖,儘量保證各個線程獲取鎖的公平
  5. 使用 Condition 對線程進行等待和喚醒

重入鎖的基本使用 lock()unlock() 分別加鎖和釋放鎖,必須一一對應。

class RetrantLockDemo implements Runnable{

	private static ReentrantLock lock = new ReentrantLock();
	
	public int i = 0;
	
	@Override
	public void run() {
		lock.lock(); //加鎖
		try {
			for (int i = 0; i < 10000; i++) {
				this.i++;
			}	
		} finally {
			lock.unlock(); //釋放鎖
		}
	}	
	
	
	public static void main(String[] args) throws InterruptedException {
		RetrantLockDemo demo = new RetrantLockDemo();
		Thread t1 = new Thread(demo);
		Thread t2 = new Thread(demo);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		System.out.println(demo.i);
	}
}

重入鎖可以響應線程的 interupt() 中斷,並且釋放鎖。可響應中斷的鎖需要使用 lockInterruptibly()

class RetrantLockDemo implements Runnable{

	private static ReentrantLock lock1 = new ReentrantLock();
	
	private static ReentrantLock lock2 = new ReentrantLock();
	
	public int i = 0;
	
	public int lock = 1;
	
	public RetrantLockDemo(int lock) {
		this.lock = lock;
	}
	
	@Override
	public void run() {
		try {
			if (this.lock == 1) {
				try {
					lock1.lockInterruptibly();
					Thread.sleep(1000);
					int a = 2;
					//這個示例此處會響應中斷,然後當前線程退出, lock()方法不能響應
					lock2.lockInterruptibly();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			} else {
				try {
					lock2.lockInterruptibly();
					Thread.sleep(1000);
					lock1.lockInterruptibly();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		} finally {
			if (lock1.isHeldByCurrentThread())
				lock1.unlock();
			if (lock2.isHeldByCurrentThread())
				lock2.unlock();
		}
	}	
	
	
	public static void main(String[] args) throws InterruptedException {
		RetrantLockDemo demo = new RetrantLockDemo(1);
		RetrantLockDemo demo2 = new RetrantLockDemo(2);
		Thread t1 = new Thread(demo);
		Thread t2 = new Thread(demo2);
		t1.start();
		t2.start();
		Thread.sleep(2000);
		t1.interrupt();
		t1.join();
		t2.join();
		System.out.println(demo.i);
	}
}

使用 tryLock(long timeout, TimeUnit unit) 可以嘗試獲取鎖,如果 timeou 時間內獲取不到則越過臨界區,放棄獲取鎖。

使用 tryLock() 嘗試獲取則不等待

class RetrantLockDemo2 implements Runnable{

	private static ReentrantLock lock = new ReentrantLock();
	
	
	@Override
	public void run() {
		try {
			if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
				Thread.sleep(6000);
				System.out.println("進入線程");
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (lock.isHeldByCurrentThread())
				lock.unlock();
		}
		System.out.println("結束");
	}	
	
	
	public static void main(String[] args) throws InterruptedException {
		RetrantLockDemo2 demo = new RetrantLockDemo2();
		Thread t1 = new Thread(demo);
		Thread t2 = new Thread(demo);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
	}
}

通常線程獲取鎖是不公平的,系統調度會傾向於已經獲得鎖的線程,這樣效率更高。
重入鎖提供了公平鎖,new ReentrantLock(true) ,看下面代碼的執行結果基本保證各個線程獲取鎖的公平

class RetrantLockDemo3 implements Runnable{

	private static ReentrantLock lock = new ReentrantLock(true);
	
	
	@Override
	public void run() {
		while (true) {
			try {
				lock.lock();
				System.out.println(Thread.currentThread().getName() + " 獲得鎖");
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				if (lock.isHeldByCurrentThread())
					lock.unlock();
			}
		}
	}	
	
	
	public static void main(String[] args) throws InterruptedException {
		RetrantLockDemo3 demo = new RetrantLockDemo3();
		Thread t1 = new Thread(demo, "線程1");
		Thread t2 = new Thread(demo, "線程2");
		t1.start();
		t2.start();
		t1.join();
		t2.join();
	}
}


結果:
線程1 獲得鎖
線程2 獲得鎖
線程1 獲得鎖
線程2 獲得鎖
線程1 獲得鎖
線程2 獲得鎖
線程1 獲得鎖
線程2 獲得鎖
線程1 獲得鎖
線程2 獲得鎖

Condition 提供了重入鎖中的 等待和喚醒功能,示例如下

public class CoditionDemo implements Runnable {

	private static ReentrantLock lock = new ReentrantLock();
	
	private static Condition condition = lock.newCondition();
	
	
	public int i = 0;
	
	
	public CoditionDemo() {
	}
	
	@Override
	public void run() {
		try {
			try {
				lock.lock();
				condition.await();  //await 讓當前線程等待,並且會釋放當前的鎖。需要在 lock 和 unlock當中使用,否則會報異常
				System.out.println("sinal");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} finally {
			if (lock.isHeldByCurrentThread())
				lock.unlock();
		}
	}	
	
	
	public static void main(String[] args) throws InterruptedException {
		CoditionDemo demo = new CoditionDemo();
		Thread t1 = new Thread(demo);
		t1.start();

		Thread.sleep(1000);
		lock.lock();

		//喚醒 condition await 的線程,注意 await 和sinal的前後執行順序。如果 sinal比 await早,那麼就永遠不能喚醒了
		//sinal也需要在 lock 和 unlock 當中使用
		condition.signalAll(); 
	
		lock.unlock();
		t1.join();
		System.out.println(demo.i);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章