CyclicBarrier和CountDownLatch的区别
CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。
CountDownLatch是减计数方式,而CyclicBarrier是加计数方式
CountDownLatch不可以复用,而CyclicBarrier可以复用。
Semaphore
Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于给线程规定一个量从而控制允许活动的线程数。
Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。
原来阐述
以一个停车场为例。加入一共只有3个停车位,那么当来了5辆车的时候,那么看门的人可以不受阻碍的放3辆车进去,其余的在入口处等候。这时候如果继续来车就都得在门外等候。
这时,如果有一辆车停车位里的车离开了,就可以放一辆进来了(至于放哪俩进来,有公平锁和非公平锁之分),如此往复。
每辆车就好比一个线程,看门人就好比一个信号量,看门人限制了可以活动的线程。
对于Semaphore类而言,就如同一个看门人,限制了可活动的线程数。
主要方法:
Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。 Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。 void acquire():从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位。 void acquire(int n):从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。比如n=2,就相当于一辆车占了两个车位。 void release():释放一个许可,将其返回给信号量。就如同车开走返回一个车位。 void release(int n):释放n个许可。 int availablePermits():当前可用的许可数。
代码示例
就以上面的提车系统,用代码里实现(仅供参考)
//控制三个停车位 private static final Semaphore semaphore = new Semaphore(3); //线程池:核心线程数可以有5个 private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor( 5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); //一辆车代表一个进程 进去提车系统 private static class CarThread extends Thread { private final String name; //品牌名称 private final int age; //使用了多少年 public CarThread(String name, int age) { this.name = name; this.age = age; } @Override public void run() { try { semaphore.acquire(); //常识去获取许可 进入停车 System.out.println(Thread.currentThread().getName() + ":大家好,我是【" + name + "】使用了【" + age + "】年,当前时间为:" + System.currentTimeMillis()); Thread.sleep(1000); //模拟停车时长 停后离开 System.out.println("【" + name + "】要准备离开停车场了,当前剩余空位【" + semaphore.availablePermits() + "】,当前时间为:" + System.currentTimeMillis()); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { String[] name = {"奔驰", "宝马", "奥迪", "大众", "尼桑", "wey派", "领克"}; int[] age = {1, 3, 5, 6, 7, 10, 12}; //一次性来了7辆车 for (int i = 0; i < 7; i++) { threadPool.execute(new CarThread(name[i], age[i])); } } 输出: pool-1-thread-3:大家好,我是【奥迪】使用了【5】年,当前时间为:1544953335062 pool-1-thread-2:大家好,我是【宝马】使用了【3】年,当前时间为:1544953335062 pool-1-thread-1:大家好,我是【奔驰】使用了【1】年,当前时间为:1544953335063 【奔驰】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336081 pool-1-thread-1:大家好,我是【wey派】使用了【10】年,当前时间为:1544953336081 【奥迪】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336085 pool-1-thread-4:大家好,我是【大众】使用了【6】年,当前时间为:1544953336088 【宝马】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336088 pool-1-thread-5:大家好,我是【尼桑】使用了【7】年,当前时间为:1544953336089 【wey派】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953337083 pool-1-thread-3:大家好,我是【领克】使用了【12】年,当前时间为:1544953337083 【大众】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953337089 【尼桑】要准备离开停车场了,当前剩余空位【1】,当前时间为:1544953337094 【领克】要准备离开停车场了,当前剩余空位【2】,当前时间为:1544953338083
private static final Semaphore semaphore = new Semaphore(3, true);
运行:
输出: pool-1-thread-2:大家好,我是【宝马】使用了【3】年,当前时间为:1544953560457 pool-1-thread-3:大家好,我是【奥迪】使用了【5】年,当前时间为:1544953560457 pool-1-thread-1:大家好,我是【奔驰】使用了【1】年,当前时间为:1544953560457 【奔驰】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561468 pool-1-thread-4:大家好,我是【大众】使用了【6】年,当前时间为:1544953561468 【奥迪】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561468 pool-1-thread-5:大家好,我是【尼桑】使用了【7】年,当前时间为:1544953561468 【宝马】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561469 pool-1-thread-1:大家好,我是【wey派】使用了【10】年,当前时间为:1544953561469 【大众】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562468 pool-1-thread-3:大家好,我是【领克】使用了【12】年,当前时间为:1544953562468 【尼桑】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562469 【wey派】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562469 【领克】要准备离开停车场了,当前剩余空位【2】,当前时间为:1544953563470
从输出的结果可以看出"大众", “尼桑”, “wey派”, "领克"是排队按照顺序进入的,这时候就是公平锁了。
Semaphore内部基于AQS的共享模式,所以实现都委托给了Sync类。 (原理其实大家可以自行参照源码,也比较简单)
new Semaphore(1)可以利用这个,间接实现单例模式
Semaphore总结
Semaphore主要用于控制当前活动线程数目,就如同停车场系统一般,而Semaphore则相当于看守的人,用于控制总共允许停车的停车位的个数。虽然我们自己也可以通过lock等手动来控制,但既然JUC为我们提供了便捷的工具,为何不使用呢?
Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。
相关面试题
解释一下CountDownLatch概念?
CountDownLatch 和CyclicBarrier的不同之处?
给出一些CountDownLatch使用的例子?
CountDownLatch 类中主要的方法?
说说你对Semaphore的理解,可以写一个示例吗?