lockInterruptibly()
中斷鎖,線程等待鎖的過程中如果被中斷,則會立刻進入該線程,響應中斷異常(異常拋出的話就進入上層處理異常)
如果沒有被中斷,則跟lock()方法一樣
package day20191203;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/5
* @Description: lockInterruptibly(),中斷鎖
* 線程等待鎖的過程中如果被中斷,則會立刻進入該線程,響應中斷
*/
public class TestLockInterruptibly {
private Lock lock = new ReentrantLock();
private void doSomething() {
try {
lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
// 睡眠5秒,能確保看得到效果
Thread.sleep(5000L);
System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "\t中斷了");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestLockInterruptibly testLockInterruptibly = new TestLockInterruptibly();
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t進來了");
testLockInterruptibly.doSomething();
}, "AA");
thread1.start();
// main主線程睡眠一會,確保線程1在線程2之前執行
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t進來了");
testLockInterruptibly.doSomething();
}, "BB");
thread2.start();
// 1秒後,中斷線程2
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
}
執行結果
AA線程1獲取到鎖之後,執行自身代碼;BB線程2等待鎖,但是此時main線程中斷了線程2,因爲是lockInterruptibly()等待鎖,所以直接響應中斷異常
lock()
線程在等待鎖的過程中,不會響應中斷(會一直等待獲取鎖),但是在中斷點(sleep)會響應之前的中斷
package day20191203;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/5
* @Description: lock.lock()
* lock,非中斷鎖,線程在等待鎖的過程中,不會響應中斷(會一直等待獲取鎖),但是在中斷點會響應中斷(sleep)
*/
public class TestLock {
private Lock lock = new ReentrantLock();
private void doSomething() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
// 睡眠5秒,能確保看得到效果
Thread.sleep(5000L);
System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "\t中斷了");
} finally {
lock.unlock();
}
}
/**
* doSomething2方法,沒有sleep,就一直不會響應中斷
*/
private void doSomething2() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "\t獲得鎖");
System.out.println(Thread.currentThread().getName() + "\t正在DoSomething");
System.out.println(Thread.currentThread().getName() + "\t釋放鎖");
} catch (Exception e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName() + "\t中斷了");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestLock testLock = new TestLock();
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t進來了");
testLock.doSomething();
}, "AA");
thread1.start();
// main主線程睡眠一會,確保線程1在線程2之前執行
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t進來了");
testLock.doSomething2();
}, "BB");
thread2.start();
// 1秒後,中斷線程2
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
}
執行結果
跟上面的區別就是:雖然main線程也中斷了BB線程2,但是由於是lock()方法等待鎖,所以並不會響應中斷,而是一直等待獲取鎖
如果在BB線程中調用sleep方法(中斷點),就會響應之前的中斷,處理InterruptedException異常,感興趣的可以自己試一試
tryLock()
是一種比較瀟灑的獲取鎖的方式
有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false;
這個方法無論如何都會立即返回,即使拿不到鎖也不會一直在那等待。
還有一個重載方法
boolean tryLock(long time, TimeUnit unit)
表示在一定時間內重試獲取鎖,獲取不到再返回
package day20191203;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: xiaoshijiu
* @Date: 2019/12/5
* @Description: lock.tryLock(),是一種比較瀟灑的獲取鎖的方式
* 有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false,
* 這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
*/
public class TestTryLock {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
new Thread(() -> {
String tName = Thread.currentThread().getName();
if (lock.tryLock()) {
System.out.println(tName + "\t獲取到鎖!");
} else {
System.out.println(tName + "\t沒獲取到鎖!");
// 獲取不到鎖,直接return掉,避免繼續執行下面,遇到unlock方法會拋出異常
return;
}
try {
for (int i = 0; i < 5; i++) {
System.out.println(tName + ":" + i);
}
Thread.sleep(1000);
} catch (Exception e) {
System.out.println(tName + "\t出錯了!!!");
} finally {
System.out.println(tName + "\t釋放鎖!!");
lock.unlock();
}
}, "AA").start();
new Thread(() -> {
String tName = Thread.currentThread().getName();
if (lock.tryLock()) {
System.out.println(tName + "\t獲取到鎖!");
} else {
System.out.println(tName + "\t獲取不到鎖!");
// 獲取不到鎖,直接return掉,避免繼續執行下面,遇到unlock方法會拋出異常
return;
}
try {
for (int i = 0; i < 5; i++) {
System.out.println(tName + ":" + i);
}
} catch (Exception e) {
System.out.println(tName + "\t出錯了!!!");
} finally {
System.out.println(tName + "\t釋放鎖!!");
lock.unlock();
}
}, "BB").start();
}
}
執行結果
需要注意的一點是:
使用tryLock()時最好使用if判斷,false時直接return;
因爲沒有獲取到鎖也會執行下面的代碼,也就會發生執行到unlock時報錯的情況
比如上面的代碼塊
BB線程2,else裏面沒有寫return
那執行結果:
可見,BB線程2也會繼續往下執行,發生的異常是unlock()那裏(沒有加鎖,何來解鎖)