CountDownLatch/CyclicBarrier/Semaphore使用过吗?

一、CountDownLatch(倒计时)

基本概念,是什么?

让一个线程await堵塞,直到另一些县城完成一系列操作后才被唤醒;

CountDownLatch主要有2个方法:当一个或者多个线程调用await方法的时候,调用线程将被堵塞;其他线程调用countDown方法将会将计数器减1(调用countDown方法的线程不会堵塞),当计数器变为0的时候,因调用await方法被堵塞的线程将会被唤醒,继续执行。(一个倒计数等待操作)

经典案例:自习室有7个人,1个班长和6个同学,现在的要求是班长必须等到其他6人陆续离开教室后,才可以关门走人

1、首先我们先尝试不使用CountDownLatch

package com.jiguiquan.www;

public class CountDownLatchDemo {
	public static void main(String[] args) {
		for (int i = 0; i < 6; i++) {
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName()+"\t 上完自习,离开教室");
			},String.valueOf(i)).start();
		}
		System.out.println(Thread.currentThread().getName()+"\t *******班长最后关门走人");
	}
}

执行结果如下:

image.png

显然,班长成功将4位同学锁在了教室过夜!

2、使用CountDownLatch

package com.jiguiquan.www;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
	public static void main(String[] args) throws Exception {
		CountDownLatch countDownLatch = new CountDownLatch(6);
		
		for (int i = 0; i < 6; i++) {
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName()+"\t 上完自习,离开教室");
				countDownLatch.countDown();
			},String.valueOf(i)).start();
		}
		
		countDownLatch.await();
		System.out.println(Thread.currentThread().getName()+"\t *******班长最后关门走人");
	}
}

执行结果如下:

image.png

显然,现在流程很正确了;

另一个案例,秦灭六国,一统华夏,使用CountDownLatch和枚举Enum类实现

CountryEnum

package com.jiguiquan.www;

public enum CountryEnum {
	ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩");
	
	private Integer retCode;
	private String retMessage;
	
	CountryEnum(Integer retCode, String retMessage) {
		this.retCode = retCode;
		this.retMessage = retMessage;
	}

	public Integer getRetCode() {
		return retCode;
	}

	public String getRetMessage() {
		return retMessage;
	}

	//根据传进来的数字,获得对应的枚举
	public static CountryEnum forEach_CountryEnum(int index) {
		CountryEnum[] myArray = CountryEnum.values();  //枚举类型天生带着的一个自己的遍历方法;
		
		for (CountryEnum item : myArray) {
			if (index == item.getRetCode()) {
				return item;
			}
		}
		return null;
	}
}

CountDownLatchDemo2

package com.jiguiquan.www;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo2 {
	public static void main(String[] args) throws Exception {
		CountDownLatch countDownLatch = new CountDownLatch(6);
		
		for (int i = 1; i <= 6; i++) {
			new Thread(() -> {
				System.out.println(Thread.currentThread().getName()+"\t国 被灭");
				countDownLatch.countDown();
			},CountryEnum.forEach_CountryEnum(i).getRetMessage()).start();
		}
		
		countDownLatch.await();
		System.out.println(Thread.currentThread().getName()+"\t 秦国统一六国");
	}
}

执行结果:

image.png


二、CyclicBarrier(集齐7颗龙珠才能召唤神龙)

基本概念,是什么?

CyclicBarrier的逻辑和CountDownLatch正好相反;字面意思是可循环(Cyclic)使用的屏障(Barrier);

当一组线程达到一个屏障(也可以叫同步点)的时候会被堵塞,直到最后一个线程到达屏障时候,屏障才会打开,所有被屏障拦截的线程才会继续干活;

初始值为0,向上增加到预期时候,才可以执行;

线程进入屏障通过CyclicBarrier的await()方法实现;

经典案例:收集7颗龙珠召唤神龙
package com.jiguiquan.www;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
	public static void main(String[] args) {
		//任何接口,如果只包含唯一一个抽象方法,那么它就是一个FI(function interface函数式接口)
		//要求,函数式接口必须要用lambda表达式,好看
		//public CyclicBarrier(int parties, Runnable barrierAction) 
		CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
			System.out.println("召唤神龙");
		});
		
		for (int i = 1; i <= 7; i++) {
			int tempInt = i;
			new Thread(()->{
				System.out.println(Thread.currentThread().getName()+"\t 收集到第"+tempInt+"颗龙珠");
				try {
					cyclicBarrier.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (BrokenBarrierException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}, String.valueOf(i)).start();
		}
	}
}

执行结果:

image.png

顺利召唤神龙


三、Semaphore(信号灯,技术信号量)

——停车场(计数方式不像上面2个,一个只减,一个只增,Semaphore有增有减,有减有增,用完归还)

Semaphore信号灯(计数信号量) 主要用于两个目的:

  • 一是用于多个共享资源的互斥使用;

  • 另一个用于并发线程数的控制;

经典案例:6部车进3个车位的停车场
package com.jiguiquan.www;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
	public static void main(String[] args) {
		Semaphore semaphore = new Semaphore(3);  //模拟3个停车位
		for (int i = 1; i <= 6; i++) {  //模拟6部汽车
			new Thread(() -> {
				try {
					semaphore.acquire();  //(抢占成功)打出这行,代表一部车已经占到一个车位了
					System.out.println(Thread.currentThread().getName()+"\t 抢到车位");
					//暂停一会儿线程,代表车在这儿停一段时间
					TimeUnit.SECONDS.sleep(3);
					System.out.println(Thread.currentThread().getName()+"\t 停车3秒钟后离开车位");
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					semaphore.release();
				}
			},String.valueOf(i)).start();
		}
	}
}

执行结果:

image.png

很显然,先抢到车位的3部车,在使用过程中,456另外3部车在场外等待;

在前三部车停完3秒钟后,会归还车位,然后另外三部车又迅速进来;

Semaphore有借有还,这种特性在以后的线程池中使用,可以达到非常完美的效果!

jiguiquan@163.com

文章作者信息...

1 Comment

  • 你好

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>

相关推荐