Locks包 類層次結(jié)構(gòu)
Lock接口
方法簽名 | 描述 | 說(shuō)明 |
---|---|---|
void lock(); | 獲取鎖(不死不休) | 一直獲取鎖,直到拿到為止 |
boolean tryLock(); | 獲取鎖(淺嘗輒止) | 嘗試獲得鎖刃永,獲取不到就算了 |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 獲取鎖(過(guò)時(shí)不候) | 超時(shí)限制货矮,超過(guò)時(shí)間就放棄 |
void lockInterruptibly() throws InterruptedException; | 獲取鎖(任人擺布) | 可以在外部通過(guò)方法中斷 |
void unlock(); | 釋放鎖 | |
Condition newCondition(); |
結(jié)論:
1、lock()最常用;
2斯够、lockInterruptibly()方法一般更昂貴囚玫,有的impl可能沒(méi)有實(shí)現(xiàn)lockInterruptibly(),只有真的需要效應(yīng)中斷時(shí)读规,才使用抓督,使用之前看看impl對(duì)該方法的描述。
- trylock
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
lock.lock(); //主線程拿到鎖
Thread th = new Thread(new Runnable() {
@Override
public void run() {
boolean rs = lock.tryLock();
System.out.println("是否獲取到鎖: " + rs);
}
});
th.start();
Thread.sleep(2000L);
th.interrupt();//中斷線程運(yùn)行
System.out.println("th 線程被中斷了");
}
}
是否獲取到鎖: false
th 線程被中斷了
- trylock帶超時(shí)
package lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
lock.lock(); //主線程拿到鎖
Thread th = new Thread(new Runnable() {
@Override
public void run() {
boolean rs = false;
try {
rs = lock.tryLock(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("是否獲取到鎖: " + rs);
}
});
th.start();
Thread.sleep(2000L);
th.interrupt();//中斷線程運(yùn)行
System.out.println("th 線程被中斷了");
}
}
是否獲取到鎖: false
th 線程被中斷了
- lockInterruptibly
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
lock.lock(); //主線程拿到鎖
Thread th = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println("獲取鎖時(shí)被中斷了");
e.printStackTrace();
}
}
});
th.start();
Thread.sleep(2000L);
th.interrupt();//中斷線程運(yùn)行
System.out.println("th 線程被中斷了");
}
}
th 線程被中斷了
獲取鎖時(shí)被中斷了
java.lang.InterruptedException
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 lock.GetLock_Demo$1.run(GetLock_Demo.java:16)
at java.lang.Thread.run(Thread.java:748)
- lock and unlock
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class GetLock_Demo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
lock.lock(); //主線程拿到鎖
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("嘗試獲得鎖");
lock.lock();
System.out.println("獲得鎖了");
}
});
th.start();
Thread.sleep(2000L);
th.interrupt();//中斷線程運(yùn)行
System.out.println("th 線程被中斷了");
Thread.sleep(5000L);
lock.unlock();
}
}
嘗試獲得鎖
th 線程被中斷了
獲得鎖了
Condition
Condition 一般是將其中的await和signal成對(duì)使用的掖桦,且一般是await在前signal在后本昏,而且調(diào)用的使用,應(yīng)該確保本身是獲取到鎖的情況下枪汪,不然會(huì)出現(xiàn)以下問(wèn)題:
1. await 和 signal 方法應(yīng)該在lock內(nèi)部調(diào)用涌穆,否則會(huì)發(fā)生 IllegalMonitorStateException
異常
package lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Condition_Demo {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
@Override
public void run() {
lock.lock();
try {
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖");
condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
thread.start();
Thread.sleep(2000L);
System.out.println("休眠2s雀久,來(lái)控制線程");
condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò)宿稀,因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò),
}
}
當(dāng)前線程:Thread-0獲得鎖
休眠2s,來(lái)控制線程
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
at lock.Condition_Demo.main(Condition_Demo.java:33)
2. signal應(yīng)該在await后調(diào)用赖捌,否則會(huì)導(dǎo)致死鎖
package lock;
import sync.ReentrantLockDemo;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Condition_Demo {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread() {
@Override
public void run() {
try {
Thread.sleep(3000L);
System.out.println("休眠3秒祝沸,等待主線程先執(zhí)行.");
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖");
condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行
System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
thread.start();
Thread.sleep(2000L);
System.out.println("休眠2s越庇,來(lái)控制線程");
lock.lock();
condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò)罩锐,因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò),
lock.unlock(); //獲取到了這把鎖,然后解鎖.
//2.當(dāng)然這里會(huì)出現(xiàn)死鎖的卤唉,如果signal方法在我們的await之前執(zhí)行涩惑,那么這里就會(huì)死鎖
}
}
休眠2s,來(lái)控制線程
休眠3秒桑驱,等待主線程先執(zhí)行.
當(dāng)前線程:Thread-0獲得鎖
// 這里死鎖了
- 使用condition實(shí)現(xiàn)阻塞隊(duì)列的例子
package lock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BlockingQueue_Demo {
public static void main(String[] args) throws InterruptedException {
BlockingQueue kaneBlockingQueue = new BlockingQueue(6);
new Thread() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
kaneBlockingQueue.put("x" + i);
}
}
}.start();
Thread.sleep(1000L);
System.out.println("開(kāi)始取元素");
for (int i = 0; i < 8; i++) {
kaneBlockingQueue.take();
Thread.sleep(2000);
}
}
}
class BlockingQueue {
List<Object> list = new ArrayList<>();
private Lock lock = new ReentrantLock();
private Condition putCondition = lock.newCondition(); //condition可以有多個(gè)竭恬,針對(duì)不同的操作放入不同condition,相當(dāng)于等待隊(duì)列
private Condition takeCondition = lock.newCondition();
private int length;
public BlockingQueue(int length) {
this.length = length;
}
public void put(Object obj) {
lock.lock(); //思考一個(gè)讀一個(gè)寫(xiě)莲镣,為什么要加鎖呢遏乔?
try {
while (true) {
if (list.size() < length) { //我們集合的長(zhǎng)度不能超過(guò)規(guī)定的長(zhǎng)度咕村,才能向里面放東西
list.add(obj);
System.out.println("隊(duì)列中放入元素:" + obj);
takeCondition.signal();
return;
} else { //如果放不進(jìn)去速那,就該阻塞. --利用condition實(shí)現(xiàn)
putCondition.await();//掛起
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public Object take() {
lock.lock();
try {
while (true) {
if (list.size() > 0) {
Object obj = list.remove(0);
System.out.println("隊(duì)列中取得元素:" + obj);
putCondition.signal();
return obj;
} else {
takeCondition.await();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
return null;
}
}
}
隊(duì)列中放入元素:x0
隊(duì)列中放入元素:x1
隊(duì)列中放入元素:x2
隊(duì)列中放入元素:x3
隊(duì)列中放入元素:x4
隊(duì)列中放入元素:x5
開(kāi)始取元素
隊(duì)列中取得元素:x0
隊(duì)列中放入元素:x6
隊(duì)列中取得元素:x1
隊(duì)列中放入元素:x7
隊(duì)列中取得元素:x2
隊(duì)列中放入元素:x8
隊(duì)列中取得元素:x3
隊(duì)列中放入元素:x9
隊(duì)列中取得元素:x4
隊(duì)列中取得元素:x5
隊(duì)列中取得元素:x6
隊(duì)列中取得元素:x7
Process finished with exit code 0
可重入鎖 ReentrantLock
一般來(lái)說(shuō),如果可重入鎖的加鎖次數(shù)是n岔绸,那么解鎖次數(shù)也得是n才能完全釋放鎖理逊,否則,如果小于n 則無(wú)法正常釋放鎖亭螟,此時(shí)如果有別的線程要加鎖挡鞍,則無(wú)法獲取到鎖而被阻塞;如果大于n预烙,則會(huì)觸發(fā) IllegalMonitorStateException
異常, ReentrantLock 默認(rèn)是使用非公平鎖道媚,如果要使用公平鎖扁掸,可以使用 new ReentrantLock(true)
來(lái)創(chuàng)建。
- 小于n的情況
package lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Reentrant_Demo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
System.out.println(Thread.currentThread().getName() + "獲得第1次鎖");
lock.lock();
System.out.println(Thread.currentThread().getName() + "獲得第2次鎖");
lock.unlock();
new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "開(kāi)始去釋放鎖");
lock.lock();
System.out.println("獲得鎖成功~~~");
lock.unlock();
}
}.start();
}
}
main獲得第1次鎖
main獲得第2次鎖
Thread-0開(kāi)始去釋放鎖
// 子線程獲取鎖失敗導(dǎo)致阻塞了
-
大于n的情況
修改成3次unlock
// 修改成3次unlock
lock.unlock();
lock.unlock();
lock.unlock();
main獲得第1次鎖
main獲得第2次鎖
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
at lock.Reentrant_Demo.main(Reentrant_Demo.java:18)
-
簡(jiǎn)單說(shuō)明圖
簡(jiǎn)單說(shuō)明圖
實(shí)現(xiàn)一個(gè)ReenrantLock的demo版本 - 一個(gè)現(xiàn)實(shí)思想的簡(jiǎn)單版本
package lock;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
public class ReentrantLock_Demo implements Lock {
//記錄鎖的擁有者
AtomicReference<Thread> owner = new AtomicReference<>();
//記錄重入次數(shù)的count
AtomicInteger count = new AtomicInteger(0);
//等待隊(duì)列
private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue();
@Override
public boolean tryLock() {
//判斷count值是否為0最域,如果count不等于0谴分,說(shuō)明鎖被占用
int ct = count.get();
//判斷鎖是不是自己占用的,做重入
if (ct != 0) {
if (Thread.currentThread() == owner.get()) {
count.set(ct + 1);
return true;
}
} else { //若count為0 镀脂,表示當(dāng)前鎖未被占用牺蹄,通過(guò)CAS操作
if (count.compareAndSet(ct, ct + 1)) {
owner.set(Thread.currentThread()); //如果不是自己,進(jìn)入隊(duì)列
return true;
}
}
return false;
}
@Override
public void lock() {
if (!tryLock()) {
//加入等待隊(duì)列
waiters.offer(Thread.currentThread());
while (true) {
//若線程是隊(duì)列頭部薄翅,先判斷一次沙兰,現(xiàn)在能不能去搶,然后再去加鎖
Thread head = waiters.peek();
if (head == Thread.currentThread()) {
if (!tryLock()) {
LockSupport.park();
} else {
waiters.poll();
return;
}
} else {
LockSupport.park();
}
}
}
}
public boolean tryUnlock() {
if (owner.get() != Thread.currentThread()) {
throw new IllegalMonitorStateException();
} else {
int ct = count.get();
int nextc = ct - 1;
count.set(nextc);
if (nextc == 0) { //可重入鎖被加鎖多次翘魄,一旦為0 就釋放鎖鼎天,如果不是0,還得繼續(xù)釋放
owner.compareAndSet(Thread.currentThread(), null);
return true;
} else {
return false;
}
}
}
@Override
public void unlock() {
if (tryUnlock()) {
Thread head = waiters.peek();
if (head != null) {
LockSupport.unpark(head);
}
}
}
/**
* 暫時(shí)忽略
*
* @throws InterruptedException
*/
@Override
public void lockInterruptibly() throws InterruptedException {
}
/**
* 暫時(shí)忽略
*
* @throws InterruptedException
*/
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
/**
* 暫時(shí)忽略
*
* @throws InterruptedException
*/
@Override
public Condition newCondition() {
return null;
}
}
如果覺(jué)得有收獲就點(diǎn)個(gè)贊吧暑竟,更多知識(shí)斋射,請(qǐng)點(diǎn)擊關(guān)注查看我的主頁(yè)信息哦~