接下來介紹比synchronized功能上更豐富的關鍵字:重入鎖java
靈活性:安全
public class ReentrantLockTest implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static int flag = 0;
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
lock.lock();
try {
flag++;
} finally {
lock.unlock();
}
}
}
public static void main(String args[]) throws InterruptedException {
ReentrantLockTest test = new ReentrantLockTest();
Thread first = new Thread(test);
Thread second = new Thread(test);
first.start();
second.start();
first.join();
second.join();
System.out.println(flag);
}
}
複製代碼
在lock.lock();
這裏,經過重入鎖保護臨界區安全,以避免發生線程安全問題。bash
在lock.unlock();
這裏,必須手動指示釋放鎖的操做,不然其餘線程將沒法得到。ide
在這段代碼裏,咱們能見到重入鎖靈活的特色。但爲何叫「重入」呢?函數
看下段代碼:性能
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
lock.lock();
lock.lock();
try {
flag++;
} finally {
lock.unlock();
lock.unlock();
}
}
}
複製代碼
由於該鎖能反覆進進出出。但要注意一下:ui
在上段代碼中,鎖是能夠重複獲取的。若是不容許,則該線程在第二次獲取鎖時會和本身產生死鎖問題。同時也要注意,線程獲取多少次鎖就要釋放多少此鎖。當獲取鎖的次數大於釋放鎖的次數、至關於該線程還持有鎖。當獲取鎖的次數少於釋放鎖的次數、則會獲得一個
java.lang.IllegalMonitorStateException
異常。this
中斷響應:spa
二話不說貼代碼:線程
public class ReentrantLockTest implements Runnable{
public static ReentrantLock producer = new ReentrantLock();
public static ReentrantLock consumer = new ReentrantLock();
public int flag = 0;
public ReentrantLockTest(int flag){
this.flag = flag;
}
@Override
public void run() {
try {
if (flag == 0) {
producer.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
consumer.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "完成工做");
} else {
consumer.lockInterruptibly();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
producer.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "完成工做");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (producer.isHeldByCurrentThread()) {
producer.unlock();
}
if (consumer.isHeldByCurrentThread()) {
consumer.unlock();
}
System.out.println(Thread.currentThread().getName() + ": 線程退出");
}
}
public static void main(String args[]) throws InterruptedException {
ReentrantLockTest producerThread = new ReentrantLockTest(1);
ReentrantLockTest consumerThread = new ReentrantLockTest(0);
Thread first = new Thread(producerThread);
Thread second = new Thread(consumerThread);
first.setName("producer");
second.setName("consumer");
first.start();
second.start();
Thread.sleep(1000);
second.interrupt();
}
}
複製代碼
這是一段容易形成死鎖的代碼,具體緣由你們應該懂。當執行到second.interrupt();
時,second線程在等待鎖時被中斷,故second線程會放棄對鎖的申請、並對已持有資源進行釋放。first線程則可以正常獲取所等待的鎖並繼續執行下去。
結果以下:
producer完成工做
java.lang.InterruptedException
consumer: 線程退出
producer: 線程退出
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at blog.ReentrantLockTest.run(ReentrantLockTest.java:22)
at java.lang.Thread.run(Thread.java:748)
複製代碼
真正完成工做的只有producer線程。
限時等待:
除了用中斷避免死鎖問題外,還能夠用限時等待鎖來避免。限時等待鎖有點像是系統自動完成線程中斷的感受。先展現下限時等待鎖的使用:
public class showWait implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
Thread.sleep(6000);
} else {
System.out.println(Thread.currentThread().getName() + " get lock failed");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
showWait test = new showWait();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("producer");
t2.setName("consumer");
t1.start();
t2.start();
}
}
複製代碼
上述代碼展現了lock.tryLock(5,TimeUnit.SECONDS);
的使用,在這裏,該方法接收兩個參數,分別是時長和計時單位。
該方法也能夠不帶參數,當不帶參數時,當前線程會嘗試獲取鎖,若是鎖未被其餘線程佔有則會申請成功並當即返回true。若是鎖被其餘線程佔用則當即返回false。這種方法不會引發線程等待,因此不會產生死鎖問題。
公平鎖:
在多大數狀況下,鎖的申請都是非公平性的,有時會形成線程飢餓問題。當咱們使用synchronized時產生的鎖是非公平性的,但咱們使用ReentrantLock時能夠經過構造函數進行指定其公平性。 public ReentrantLock(boolean fair)
當參數爲true時爲公平鎖,默認爲非公平鎖。公平鎖看起來挺優美的,但其必然要維護一個等待隊列,其性能必然會下降
整理:
你們回顧下這幾個方法吧。