日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

并發(fā)編程--CountdownLatch && CyclicBarrier

作者:咖啡不苦** 更新時(shí)間: 2022-06-08 編程語言

Semaphore,CountdownLatch,CyclicBarrier都是java提供的同步輔助類。
上一篇對(duì)Semaphore有了一定的了解,本篇?jiǎng)t針對(duì)CountdownLatch以及CyclicBarrier進(jìn)行一定的總結(jié)。

一、CountdownLatch

1)CountdownLatch是什么?

CyclicBarrier的字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier)
能夠使一個(gè)線程等待其他線程完成各自的工作之后再執(zhí)行。例如zookeeper分布式鎖的實(shí)現(xiàn)。而Semaphore則主要強(qiáng)調(diào)的是資源有限。

2)CountdownLatch使用場(chǎng)景

比如老公陪同媳婦去醫(yī)院,媳婦等候醫(yī)生看病,老公則針對(duì)單子去拿藥,兩者都完工,則一起回家。再比如老板等10個(gè)員工開會(huì),只有10個(gè)員工都到齊了,會(huì)議才能開始等等場(chǎng)景。

3)應(yīng)用demo

舉例說明老板等待員工開會(huì)的場(chǎng)景:

public static void main(String[] args) {
		//老板需要等待15個(gè)員工會(huì)議室開會(huì)
		final CountDownLatch latch = new CountDownLatch(15);
		for (int i = 0; i < 15; i++) {
			Random random = new Random();
			final int timer = random.nextInt(1000);
			new Thread(() -> {
				try {
					System.out.println("子線程" + Thread.currentThread().getName() + "正在趕路");
					//模仿每個(gè)員工走自己線程需要的時(shí)間
					Thread.sleep(1000 + timer);
					//調(diào)用latch的countDown方法使計(jì)數(shù)器-1;一共15個(gè)
					latch.countDown();
					System.out.println("子線程" + Thread.currentThread().getName() + "到會(huì)議室了");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}).start();
		}

		try {
			System.out.println("領(lǐng)導(dǎo)等待員工會(huì)議室開會(huì)...");
			//主線程阻塞等待計(jì)數(shù)器歸零
			latch.await();
			System.out.println("員工都來了,會(huì)議開始");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

執(zhí)行結(jié)果:

子線程Thread-1正在趕路
子線程Thread-3正在趕路
子線程Thread-0正在趕路
子線程Thread-2正在趕路
子線程Thread-4正在趕路
子線程Thread-5正在趕路
子線程Thread-6正在趕路
子線程Thread-7正在趕路
子線程Thread-8正在趕路
子線程Thread-9正在趕路
子線程Thread-10正在趕路
子線程Thread-11正在趕路
子線程Thread-12正在趕路
子線程Thread-13正在趕路
領(lǐng)導(dǎo)等待員工會(huì)議室開會(huì)…
子線程Thread-14正在趕路
子線程Thread-5到會(huì)議室了
子線程Thread-1到會(huì)議室了
子線程Thread-12到會(huì)議室了
子線程Thread-13到會(huì)議室了
子線程Thread-4到會(huì)議室了
子線程Thread-6到會(huì)議室了
子線程Thread-2到會(huì)議室了
子線程Thread-0到會(huì)議室了
子線程Thread-3到會(huì)議室了
子線程Thread-10到會(huì)議室了
子線程Thread-14到會(huì)議室了
子線程Thread-8到會(huì)議室了
子線程Thread-9到會(huì)議室了
子線程Thread-7到會(huì)議室了
子線程Thread-11到會(huì)議室了
員工都來了,會(huì)議開始

4)源碼分析

核心關(guān)注方法只有兩個(gè),latch.countDown();和latch.await();
countDown() 方法每次調(diào)用都會(huì)將 state 減 1,直到state 的值為 0;而 await 是一個(gè)阻塞方法,當(dāng) state 減 為 0 的時(shí)候,await 方法才會(huì)返回。await 可以被多個(gè)線程調(diào)用,大家在這個(gè)時(shí)候腦子里要有個(gè)圖:所有調(diào)用了await 方法的線程阻塞在 AQS 的阻塞隊(duì)列中,等待條件滿(state == 0),將線程從隊(duì)列中一個(gè)個(gè)喚醒過來。

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

在 CountDownLatch 內(nèi)部寫了一個(gè) Sync 并且繼承了 AQS 這個(gè)抽象類重寫了 AQS中的共享鎖方法。這段代碼主要是判定當(dāng)前線程是否獲取到了共享鎖
在這里插入圖片描述

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //state 如果不等于 0,說明當(dāng)前線程需要加入到共享鎖隊(duì)列中
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
//在共享可中斷模式下獲取。
private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        //創(chuàng)建一個(gè)共享模式的節(jié)點(diǎn)添加到隊(duì)列中
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    //嘗試獲取鎖
                    int r = tryAcquireShared(arg);
                    //r>=0 表示獲取到了執(zhí)行權(quán)限,這個(gè)時(shí)候因?yàn)?state!=0,所以不會(huì)執(zhí)行這段代碼
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //阻塞線程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

二、CyclicBarrier

1)CyclicBarrier是什么?

柵欄屏障,讓一組線程到達(dá)一個(gè)屏障(也可以叫同步點(diǎn))時(shí)被阻塞,直到最后一個(gè)線程到達(dá)屏障時(shí),屏障才會(huì)開門,所有被屏障攔截的線程才會(huì)繼續(xù)運(yùn)行。
簡(jiǎn)單理解就是:多個(gè)線程之間互相等待,滿足條件在同一時(shí)間進(jìn)行。

2)CyclicBarrier使用場(chǎng)景

可以用于多線程計(jì)算數(shù)據(jù),最后合并計(jì)算結(jié)果的場(chǎng)景。
又比如旅行團(tuán)旅行,需要大家都到齊,證件都辦理好,才能出發(fā)。

3)應(yīng)用demo

旅行團(tuán)旅行,需要大家都到齊,證件都辦理好,上旅行車才能出發(fā)。

public class CyclicBarrierTest1 extends Thread{
	private final CyclicBarrier barrier;
	//隨機(jī)值處理
	private final Random random = new Random();
	public CyclicBarrierTest1(String name,CyclicBarrier barrier) {
		super(name);
		this.barrier = barrier;
	}

	/**
	 * 重寫run方法
	 */
	@Override
	public void run() {
		try {
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 已經(jīng)到達(dá)旅行團(tuán)");
			barrier.await();
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 證件已經(jīng)辦理好");
			barrier.await();
			Thread.sleep(random.nextInt(2000));
			System.out.println(Thread.currentThread().getName() + " - 已經(jīng)上旅行車");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (BrokenBarrierException e) {
			e.printStackTrace();
		}
		super.run();
	}

	public static void main(String[] args) {
		//旅行團(tuán)5個(gè)游客
		CyclicBarrier cyclicBarrier=new CyclicBarrier(5);
		for (int i=0;i<5;i++){
			new CyclicBarrierTest1("游客-" + (i + 1), cyclicBarrier).start();
		}
	}
}

游客-5 - 已經(jīng)到達(dá)旅行團(tuán)
游客-3 - 已經(jīng)到達(dá)旅行團(tuán)
游客-2 - 已經(jīng)到達(dá)旅行團(tuán)
游客-1 - 已經(jīng)到達(dá)旅行團(tuán)
游客-4 - 已經(jīng)到達(dá)旅行團(tuán)
游客-2 - 證件已經(jīng)辦理好
游客-4 - 證件已經(jīng)辦理好
游客-1 - 證件已經(jīng)辦理好
游客-3 - 證件已經(jīng)辦理好
游客-5 - 證件已經(jīng)辦理好
游客-1 - 已經(jīng)上旅行車
游客-2 - 已經(jīng)上旅行車
游客-3 - 已經(jīng)上旅行車
游客-5 - 已經(jīng)上旅行車
游客-4 - 已經(jīng)上旅行車

原文鏈接:https://blog.csdn.net/huo065000/article/details/120649318

欄目分類
最近更新