Thread-1 赶上了1趟班车。
Thread-10 赶上了1趟班车。
Thread-20 赶上了1趟班车。
Thread-81 赶上了1趟班车。
Thread-98 赶上了1趟班车。
Thread-95 赶上了1趟班车。
Thread-41 赶上了1趟班车。
Thread-82 赶上了1趟班车。
Thread-56 赶上了1趟班车。
Thread-37 赶上了1趟班车。
Thread-74 赶上了1趟班车。
Thread-2 赶上了1趟班车。
车上座位已满,请等待下一班!
Thread-12 赶上了2趟班车。
Thread-13 赶上了2趟班车。
Thread-68 赶上了2趟班车。
Thread-80 赶上了2趟班车。
Thread-40 赶上了2趟班车。
Thread-39 赶上了2趟班车。
Thread-14 赶上了2趟班车。
Thread-87 赶上了2趟班车。
Thread-76 赶上了2趟班车。
Thread-28 赶上了2趟班车。
Thread-26 赶上了2趟班车。
Thread-51 赶上了2趟班车。
Thread-19 赶上了2趟班车。
Thread-32 赶上了2趟班车。
Thread-22 赶上了2趟班车。
Thread-34 赶上了2趟班车。
Thread-52 赶上了2趟班车。
Thread-9 赶上了2趟班车。
Thread-55 赶上了2趟班车。
Thread-78 赶上了2趟班车。
车上座位已满,请等待下一班!
Thread-23 赶上了3趟班车。
Thread-94 赶上了3趟班车。
Thread-3 赶上了3趟班车。
Thread-4 赶上了3趟班车。
Thread-11 赶上了3趟班车。
Thread-54 赶上了3趟班车。
Thread-30 赶上了3趟班车。
Thread-46 赶上了3趟班车。
Thread-33 赶上了3趟班车。
Thread-61 赶上了3趟班车。
Thread-58 赶上了3趟班车。
Thread-47 赶上了3趟班车。
Thread-65 赶上了3趟班车。
Thread-72 赶上了3趟班车。
Thread-5 赶上了3趟班车。
Thread-96 赶上了3趟班车。
Thread-92 赶上了3趟班车。
Thread-88 赶上了3趟班车。
Thread-18 赶上了3趟班车。
Thread-83 赶上了3趟班车。
车上座位已满,请等待下一班!
Thread-7 赶上了4趟班车。
Thread-64 赶上了4趟班车。
Thread-90 赶上了4趟班车。
Thread-62 赶上了4趟班车。
Thread-17 赶上了4趟班车。
Thread-49 赶上了4趟班车。
Thread-16 赶上了4趟班车。
Thread-45 赶上了4趟班车。
Thread-38 赶上了4趟班车。
Thread-15 赶上了4趟班车。
Thread-50 赶上了4趟班车。
Thread-66 赶上了4趟班车。
Thread-59 赶上了4趟班车。
Thread-25 赶上了4趟班车。
Thread-35 赶上了4趟班车。
Thread-43 赶上了4趟班车。
Thread-8 赶上了4趟班车。
Thread-77 赶上了4趟班车。
Thread-97 赶上了4趟班车。
Thread-53 赶上了4趟班车。
车上座位已满,请等待下一班!
Thread-27 赶上了5趟班车。
Thread-21 赶上了5趟班车。
Thread-71 赶上了5趟班车。
Thread-75 赶上了5趟班车。
Thread-85 赶上了5趟班车。
Thread-73 赶上了5趟班车。
Thread-67 赶上了5趟班车。
Thread-91 赶上了5趟班车。
Thread-79 赶上了5趟班车。
Thread-44 赶上了5趟班车。
Thread-36 赶上了5趟班车。
Thread-24 赶上了5趟班车。
Thread-0 赶上了5趟班车。
Thread-60 赶上了5趟班车。
Thread-70 赶上了5趟班车。
Thread-31 赶上了5趟班车。
Thread-63 赶上了5趟班车。
Thread-29 赶上了5趟班车。
Thread-42 赶上了5趟班车。
Thread-86 赶上了5趟班车。
CyclicBarrier 就像一个屏障,实例化的时候需要传入两个参数,第一个参数指定我们的屏障最多拦截多少个线程后就打开屏障,第二个参数指明最后一个到达屏障的线程需要额外做的操作。
一般而言,最后一个线程到达屏障后,屏障将会打开,释放前面所有的线程,并在最后重新关上屏障。
CyclicBarrier 只需要用到一个 await 就可以完成所有的功能,我们总结下该方法的实现逻辑:
1、首先,减少一次可用资源数量。
2、如果可用资源数为零,则说明自己是最后一个线程,于是会执行我们传入的额外操作,唤醒所有已经到达在等待的线程,并重新开启一个屏障计数。
3、否则说明自己不是最后一个线程,于是将自身线程在一个循环当中阻塞到一个条件队列上。
Semaphore又名信号量,是操作系统中的一个概念,在Java并发编程中,信号量控制的是线程并发的数量。
作用:Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一个阻塞的acquire方法。然而,其实并没有实际的许可证这个对象,Semaphore只是维持了一个可获得许可证的数量,主要控制同时访问某个特定资源的线程数量,多用在流量控制。
注意:其他Semaphore的底层实现就是基于AQS的共享锁实现的。
如果一个线程要访问共享资源,必须先获得信号量,如果信号量的计数器值大于1,意味
着有共享资源可以访问,则使其计数器值减去1,再访问共享资源。如果计数器值为0,线
程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加1,之前进入休眠的线程将被唤醒并再次试图获得信号量。
Semaphore 适用于什么样的使用场景呢,我们举个通俗的例子:
Semaphore可以用于做流量控制,特别是公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发地读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有10个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,就可以使用Semaphore来做流量控制。
package com.concurrent.util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
-
@author riemann
-
@date 2019/07/27 23:31
*/
public class SemaphoreDemo {
private static final int THREAD_COUNT = 30;
private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
private static Semaphore semaphore = new Semaphore(10);
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 连接成功,保存数据。");
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + " 释放连接。");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
threadPool.shutdown();
}
}
输出结果:
pool-1-thread-2 连接成功,保存数据。
pool-1-thread-3 连接成功,保存数据。
pool-1-thread-1 连接成功,保存数据。
pool-1-thread-4 连接成功,保存数据。
pool-1-thread-6 连接成功,保存数据。
pool-1-thread-7 连接成功,保存数据。
pool-1-thread-8 连接成功,保存数据。
pool-1-thread-10 连接成功,保存数据。
pool-1-thread-5 连接成功,保存数据。
pool-1-thread-9 连接成功,保存数据。
pool-1-thread-10 释放连接。
pool-1-thread-14 连接成功,保存数据。
pool-1-thread-2 释放连接。
pool-1-thread-15 连接成功,保存数据。
pool-1-thread-9 释放连接。
pool-1-thread-12 连接成功,保存数据。
pool-1-thread-8 释放连接。
pool-1-thread-16 连接成功,保存数据。
pool-1-thread-3 释放连接。
pool-1-thread-11 连接成功,保存数据。
pool-1-thread-14 释放连接。
pool-1-thread-18 连接成功,保存数据。
pool-1-thread-18 释放连接。
pool-1-thread-19 连接成功,保存数据。
pool-1-thread-7 释放连接。
pool-1-thread-20 连接成功,保存数据。
pool-1-thread-5 释放连接。
pool-1-thread-17 连接成功,保存数据。
pool-1-thread-6 释放连接。
pool-1-thread-13 连接成功,保存数据。
pool-1-thread-13 释放连接。
pool-1-thread-21 连接成功,保存数据。
pool-1-thread-1 释放连接。
pool-1-thread-22 连接成功,保存数据。
pool-1-thread-4 释放连接。
pool-1-thread-23 连接成功,保存数据。
pool-1-thread-12 释放连接。
pool-1-thread-24 连接成功,保存数据。
pool-1-thread-22 释放连接。
pool-1-thread-25 连接成功,保存数据。
pool-1-thread-19 释放连接。
pool-1-thread-26 连接成功,保存数据。
pool-1-thread-17 释放连接。
pool-1-thread-27 连接成功,保存数据。
pool-1-thread-15 释放连接。
pool-1-thread-28 连接成功,保存数据。
pool-1-thread-11 释放连接。
pool-1-thread-30 连接成功,保存数据。
pool-1-thread-26 释放连接。
pool-1-thread-29 连接成功,保存数据。
pool-1-thread-24 释放连接。
pool-1-thread-16 释放连接。
pool-1-thread-28 释放连接。
pool-1-thread-29 释放连接。
pool-1-thread-20 释放连接。
pool-1-thread-27 释放连接。
pool-1-thread-21 释放连接。
pool-1-thread-25 释放连接。
pool-1-thread-30 释放连接。
pool-1-thread-23 释放连接。
在代码中,虽然有30个线程在执行,但是只允许10个并发执行。Semaphore的构造方法Semaphore(int permits)接受一个整型的数字,表示可用的许可证数量。Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。
Semaphore的用法也很简单,首先线程使用Semaphore的acquire()方法获取一个许可证,使用完之后调用release()方法归还许可证。还可以用tryAcquire()方法尝试获取许可证。
Semaphore还提供一些其他方法,具体如下:
int availablePermits():返回此信号量中当前可用的许可证数。
int getQueueLength():返回正在等待获取许可证的线程数。
boolean hasQueuedThreads():是否有线程正在等待获取许可证。
void reducePermits(int reduction):减少reduction个许可证,是个protected方法。
Collection getQueuedThreads():返回所有等待获取许可证的线程集合,是个protected方法。
Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。
下面来看一下Exchanger的应用场景:
1、Exchanger可以用于遗传算法,遗传算法里需要选出两个人作为交配对象,这时候会交换两人的数据,并使用交叉规则得出2个交配结果。
2、Exchanger也可以用于校对工作,比如我们需要将纸制银行流水通过人工的方式录入成电子银行流水,为了避免错误,采用AB岗两人进行录入,录入到Excel之后,系统需要加载这两个Excel,并对两个Excel数据进行校对,看看是否录入一致。
package com.concurrent.util;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
-
@author riemann
-
@date 2019/07/27 23:49
*/
public class ExchangerDemo {
private static final Exchanger exchanger = new Exchanger();
private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
public static void main(String[] args) {
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String A = “银行流水100”;// A录入银行流水数据
String B = exchanger.exchange(A);
System.out.println("A的视角:A和B数据是否一致: " + A.equals(B) +
",A录入的是: " + A + ",B录入是: " + B + “。”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
try {
String B = “银行流水200”;// B录入银行流水数据
String A = exchanger.exchange(B);
System.out.println("B的视角:A和B数据是否一致: " + A.equals(B) +
",A录入的是: " + A + ",B录入是: " + B + “。”);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
输出结果:
B的视角:A和B数据是否一致: false,A录入的是: 银行流水100,B录入是: 银行流水200。
A的视角:A和B数据是否一致: false,A录入的是: 银行流水100,B录入是: 银行流水200。
如果两个线程有一个没有执行exchange()
方法,则会一直等待,如果担心有特殊情况发生,避免一直等待,可以使用exchange(V x,longtimeout,TimeUnit unit)
设置最大等待时长。
五、CyclicBarrier和CountDownLatch的区别
第一个区别:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/84e603899cf84f68f38cdffe39c3fa81.jpeg)
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
75637)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/84e603899cf84f68f38cdffe39c3fa81.jpeg)
最后
现在正是金三银四的春招高潮,前阵子小编一直在搭建自己的网站,并整理了全套的**【一线互联网大厂Java核心面试题库+解析】:包括Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等**
[外链图片转存中…(img-gfKU26mz-1713445075639)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!