鎖
原理
基于将多線程并行任務通過某一種機制實作線程的串行執行,進而達到線程安全性的目的。
簡介
在 Lock 之前,隻能基于 synchronized 關鍵字來解決并發安全問題。但是 synchronized 不适合于所有的并發場景。Lock 可以解決而且更加靈活
ReentrantLock
表示重入鎖(互斥鎖),它是唯一一個實作了 Lock 接口的類。重入鎖指的是線程在獲得鎖之後,再次擷取該鎖不需要阻塞,而是直接關聯一次計數器增加重入次數,目的是避免線程的死鎖。大部分情況下可以使用 synchronized,性能上并無太大差别,使用ReentrantLock要特别注意在finally中釋放鎖,不然容易出現死鎖。
特性
- 可指定公平和非公平鎖
- 提供Condition類,可分組喚醒需要喚醒的線程
- 提供能夠中斷等待鎖的線程
- 自旋,避免鎖進入核心态
- …
code
public class ReentrantLockDemo {
// 請求總數
public static int clientTotal = 5000;
// 同時并發執行的線程數
public static int threadTotal = 200;
public static int count = 0;
private final static Lock lock = new ReentrantLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
lock.lock();
try {
count++;
} finally {
// 一定要記得在finally中釋放鎖
lock.unlock();
}
}
}
ReentrantReadWriteLock
code
public class LockExample3 {
private final Map<String, Data> map = new TreeMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public Data get(String key) {
// 讀取資料用讀鎖
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public Set<String> getAllKeys() {
// 讀取資料用讀鎖
readLock.lock();
try {
return map.keySet();
} finally {
readLock.unlock();
}
}
public Data put(String key, Data value) {
// 寫入資料用寫鎖
writeLock.lock();
try {
return map.put(key, value);
} finally {
readLock.unlock();
}
}
class Data {
}
}
StampedLock
code
public class StampedLockDemo {
// 請求總數
public static int clientTotal = 5000;
// 同時并發執行的線程數
public static int threadTotal = 200;
public static int count = 0;
private final static StampedLock lock = new StampedLock();
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
long stamp = lock.writeLock();
try {
count++;
} finally {
lock.unlock(stamp);
}
}
}