一、ReentrantLock
我們都知道鎖是為了保護臨界區(qū)資源被多線程并發(fā)訪問時的安全性。 而 ReentrantLock是JAVA提供的完全可以替代Synchronized的方案。在JDK6.0以前Synchronized的執(zhí)行效率遠遠比ReentrantLock的執(zhí)行效率差。而在JDK6.0以后對Synchronized的優(yōu)化后,ReentrantLock與Synchronized的執(zhí)行效率差距不大。
二、ReentrantLock的lock與unlock
- ReentrantLock的lock與unlock是一套靈活的保護臨界區(qū)資源的方法。我們需要注意如果在使用lock的時候,我們必須要執(zhí)行unlock操作。如果我們只調(diào)用了lock而沒有調(diào)用unlock的釋放鎖的情況下,會造成其他線程獲取不到鎖,而導致阻塞。
public class ReentrantLockDemo implements Runnable{
public ReentrantLock lock = new ReentrantLock();
public static int count = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
//通過重入鎖,防止并發(fā)增加Count的值
lock.lock();
count++;
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用重入鎖,一定要解鎖,如果沒有解鎖會導致其他線程不可以訪問。
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
Thread t1 = new Thread(reentrantLockDemo);
Thread t2 = new Thread(reentrantLockDemo);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Over Count="+count);
}
}
- ReentrantLock有一種特性就是ReentrantLock的鎖可以反復的進入,而這種反復的進入僅限于統(tǒng)一線程下,也就是說我們可以將代碼改成下面這種形式。但是我們要記住一點,重復進入鎖調(diào)時,一定要釋放鎖.
public class ReentrantLockDemo implements Runnable{
public ReentrantLock lock = new ReentrantLock();
public static int count = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
//通過重入鎖,防止并發(fā)增加Count的值
lock.lock();
//重復進入鎖
lock.lock();
count++;
} catch (Exception e) {
e.printStackTrace();
} finally {
//使用重入鎖,一定要解鎖,如果沒有解鎖會導致其他線程不可以訪問。
lock.unlock();
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();
Thread t1 = new Thread(reentrantLockDemo);
Thread t2 = new Thread(reentrantLockDemo);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Over Count="+count);
}
}
二、ReentrantLock的lockInterruptibly
我們知道使用Synchronized的時候,如果鎖被其他線程獲取。而當前線程只能等在其他線程釋放鎖,并且獲得鎖才會執(zhí)行,沒有獲得鎖時處于阻塞狀態(tài)。而ReentrantLock提供了lockInterruptibly來優(yōu)化這種情況。就是通過中斷鎖。例如我們在死鎖的情況下可以通過中斷其中一個線程的鎖來解決這種死鎖的狀態(tài)。
public class IntReentrantLock implements Runnable {
public ReentrantLock r1 = new ReentrantLock();
public ReentrantLock r2 = new ReentrantLock();
int lock = 0;
//控制死鎖
public IntReentrantLock(int lock) {
this.lock = lock;
}
@Override
public void run() {
try {
if(lock == 1){
//使用lockInterruptibly表明當前的鎖可以被終止.
r1.lockInterruptibly();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
r2.lockInterruptibly();
}else{
r2.lockInterruptibly();
try {
//線程在睡眠的時候被中斷,會拋出InterruptedException異常
Thread.sleep(5000);
} catch (InterruptedException e) {
}
r1.lockInterruptibly();
}
}catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(r1.isHeldByCurrentThread()){
r1.unlock();
}
if(r2.isHeldByCurrentThread()){
r2.unlock();
}
System.out.println("Thread :"+Thread.currentThread().getId()+"-"+Thread.currentThread().getName()+":當前線程退出");
}
}
public static void main(String[] args) throws InterruptedException {
IntReentrantLock testLock1 = new IntReentrantLock(1);
IntReentrantLock testLock2 = new IntReentrantLock(2);
Thread t1 = new Thread(testLock1,"線程1");
Thread t2 = new Thread(testLock2,"線程2");
t1.start();
t2.start();
Thread.sleep(1000);
//中斷線程2
t2.interrupt();
}
}
輸出結(jié)果:
Thread :11-線程2:當前線程退出
Thread :10-線程1:當前線程退出
上面的代碼是通過模擬死鎖的狀態(tài)。testLock1調(diào)用的時候需要用到testLock2 的鎖,testLock2 調(diào)用的時候需要用到testLock1的鎖,而雙方有拿著各自的鎖。導致死鎖。我們通過對線程t2進行終端從而解決這種死鎖的狀態(tài)。
三、ReentrantLock的trylock
除了中斷鎖通知的這種操作處理死鎖的這種情況,我們也可以通過設(shè)置獲取鎖的等待時間來進行處理。
public class TryLock implements Runnable {
public ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
//等待獲取鎖
if (lock.tryLock(3, TimeUnit.SECONDS)) {
System.out.println("get Lock Success");
Thread.sleep(5000);
} else {
System.out.println("get Lock Faild");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
System.out.println("releas Lock Success");
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
TryLock tryLock = new TryLock();
Thread t1 = new Thread(tryLock);
Thread t2 = new Thread(tryLock);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
輸出結(jié)果:
get Lock Success
get Lock Faild
releas Lock Success
四、ReentrantLock的condition
我們在使用Synchronized的時候,當某些時候需要在雙線程的協(xié)同處理的時候會使用到了wait()和notify()進行處理這種協(xié)同處理。而ReentrantLock也提供了這種協(xié)同處理就是await()和signal()。
public class ReentrantLockCondition implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();
@Override
public void run() {
try {
lock.lock();
condition.await();
System.out.println("out lock wait");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockCondition reentrantLockCondition= new ReentrantLockCondition();
Thread t1 = new Thread(reentrantLockCondition);
t1.start();
Thread.sleep(2000);
lock.lock();
//喚醒t1線程的等待
condition.signal();
System.out.println("signal Thread t1");
//釋放當前的主線程的鎖,如果沒有釋放則喚醒的t1是不可能獲取到鎖,而t1喚醒后獲取不到鎖會處理阻塞狀態(tài)
lock.unlock();
}
}
輸出結(jié)果:
signal Thread t1
out lock wait