ReadWriteLock 多寫(xiě)鎖
- ReadWriteLock 是JDK5中提供的讀寫(xiě)分離鎖,讀寫(xiě)分離可以有效的幫助減少鎖競(jìng)爭(zhēng)。用來(lái)提高系統(tǒng)性能。
讀寫(xiě)鎖的訪問(wèn)約束情況
| 讀 | 寫(xiě) | |
|---|---|---|
| 讀 | 非阻塞 | 阻塞 |
| 寫(xiě) | 堵塞 | 阻塞 |
- 讀讀 之間不互斥:讀讀之間不阻塞
- 讀-寫(xiě)互斥:讀阻塞寫(xiě),寫(xiě)也會(huì)阻塞讀
- 寫(xiě)- 寫(xiě) 互斥: 寫(xiě)寫(xiě)堵塞。
public class ReadWriteLockDemo {
private static Lock lock = new ReentrantLock();
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private static Lock readLock = readWriteLock.readLock();
private static Lock writeLock =readWriteLock.writeLock();
private int value ;
public Object handleRead(Lock lock) throws InterruptedException {
try {
lock.lock();
Thread.sleep(1000);
return value;
}finally {
lock.unlock();
}
}
public void handleWrite(Lock lock,int index) throws InterruptedException{
try {
lock.lock(); //模擬寫(xiě)的操作
Thread.sleep(1000);
value=index;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
final ReadWriteLockDemo demo= new ReadWriteLockDemo();
Runnable readRunnable =new Runnable() {
public void run() {
try {
// demo.handleRead(readLock);
demo.handleRead(lock);
} catch (Exception e) {
e.printStackTrace();
}
}
};
Runnable writerRunnable =new Runnable() {
public void run() {
try {
// demo.handleWrite(writeLock, new Random().nextInt());
demo.handleWrite(lock, new Random().nextInt());
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 18; i < 20; i++) {
new Thread(writerRunnable).start();
}
}
}
- 在代碼中我們使用了兩種方式 如果使用讀寫(xiě)鎖的該程序執(zhí)行可以在2秒內(nèi)執(zhí)行完畢,但是如果是lock鎖的沒(méi)有進(jìn)行多寫(xiě)分離,那么會(huì)在將近20秒的時(shí)間內(nèi)完成。為什么會(huì)這么長(zhǎng)時(shí)間 ,是因?yàn)樗械淖x線程與寫(xiě)線程之間 必須都互相等待 ,導(dǎo)致的。讀寫(xiě)分離之后 讀讀之間不用互相等待 ,大大減少時(shí)間。
倒計(jì)時(shí)器:CountDownLatch
- CountDownLatch 是一個(gè)非常實(shí)用的多線程控制工具類(lèi)。主要是用來(lái)控制線程等待,它可以讓某一個(gè)線程等待到知道倒計(jì)時(shí)結(jié)束再開(kāi)始執(zhí)行。代碼演示如下:
public class CountDownLatchDemo implements Runnable {
static final CountDownLatch end = new CountDownLatch(10);
static final CountDownLatchDemo demo = new CountDownLatchDemo();
@Override
public void run() {
try {
//模擬檢查任務(wù)
Thread.sleep(new Random().nextInt(10)*1000);
System.out.println("check complete");
end.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
ExecutorService exec = new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
for(int i = 0 ; i < 10 ; i++){
exec.submit(demo);
}
//等待檢查
end.await();
// 發(fā)射 相當(dāng)于開(kāi)始執(zhí)行任務(wù)
System.out.println("fire");
exec.shutdown();
}
}

結(jié)果圖
- 在上述代碼中 計(jì)數(shù)器數(shù)量為10 ,那么就是需要有10個(gè)線程完成任務(wù),在CountDownLatch 上的線程才能繼續(xù)執(zhí)行。 在代碼中我們能看到有個(gè)countdown()方法, 就是用來(lái)通知CountDownLatch, 如果一個(gè)線程完成任務(wù),那么倒計(jì)時(shí)器就可以減1.。 我們?cè)赼wait 方法,要求主線程等待所有10個(gè)檢查任務(wù)全部完成,那么主線程才會(huì)繼續(xù)執(zhí)行。

示意圖
循環(huán)柵欄 :CyclicBarrier
- 這是一種不同于 CountDOwnLatch 的多線程并發(fā)控制工具,但是也可以實(shí)現(xiàn)線程間的計(jì)數(shù)等待。它比CountDownLatch 功能更加強(qiáng)大且復(fù)雜。
- 循環(huán)柵欄 意思就是循環(huán),如果我們使用的是其計(jì)數(shù)器的功能那么如果是20個(gè),等到計(jì)數(shù)器歸0 之后還會(huì)湊齊下一批20個(gè)線程,再次形成柵欄。代碼如下:
public class CyclicBarrierDemo {
public static class Soldier implements Runnable{
private String soldier;
private final CyclicBarrier cyclic ;
public Soldier( CyclicBarrier cyclic , String soldier) {
this.cyclic = cyclic ;
this.soldier = soldier ;
}
@Override
public void run() {
try {
//等到所有任務(wù)的到來(lái)
cyclic.await() ;
doWork();
//等待所有任務(wù)完成
cyclic.await();
} catch (Exception e) {
// TODO: handle exception
}
}
void doWork(){
try {
Thread.sleep(Math.abs(new Random().nextInt()%10_000));
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class BarrierRun implements Runnable {
boolean flag ;
int N ;
public BarrierRun(boolean flag, int n) {
this.flag = flag;
N = n;
}
@Override
public void run() {
if(flag){
System.out.println("司令 : 士兵"+ N + "個(gè),任務(wù)完成");
}else{
System.out.println("司令 : 士兵"+ N + "個(gè),集合完畢");
}
}
}
public static void main(String[] args) {
final int N = 10 ;
Thread[] allSoldier = new Thread[N];
boolean flag = false ;
CyclicBarrier cyclic =new CyclicBarrier(N, new BarrierRun(flag, N));
//設(shè)置屏障點(diǎn),主要是為了執(zhí)行這個(gè)方法
System.out.println("集合隊(duì)伍!");
for(int i = 0 ; i < N ; i++ ){
System.out.println("士兵 "+ i + " 報(bào)道 ");
allSoldier[i] = new Thread(new Soldier(cyclic, " 士兵 " + i));
allSoldier[i].start();
}
}
}
-
根據(jù)代碼中,我們可以發(fā)現(xiàn) 執(zhí)行await 方法的時(shí)候 可能會(huì)拋出異常, 一個(gè)是等待異常InterruptedException,這個(gè)是方便相應(yīng) 外部緊急事件,另外一個(gè)異常是BrokenBarrierException ,這個(gè)表示 CyclicBarrier 已經(jīng)損壞, 系統(tǒng)沒(méi)法等到所有線程然后再開(kāi)始執(zhí)行,處理是把所有線程中斷 。就避免 線程的永久的等待了
執(zhí)行結(jié)果 -
整個(gè)過(guò)程的流程圖如下:
示意圖

