举例详解 java.util.concurrent 并发包 4 种常见类
Posted 满眼*星辰
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了举例详解 java.util.concurrent 并发包 4 种常见类相关的知识,希望对你有一定的参考价值。
目录
JUC
具体就是指 :java.util.concurrent 的并发包
1. 可重入互斥锁 ReentrantLock
作用
保证线程安全,和sychronized使用差不所
常用方法
ReentrantLock():构造方法,参数为true则为公平锁,默认非公平锁
lock():加锁
unlock():释放锁
代码举例
//全局变量
private static int number = 0;
//循环次数
private static final int maxSize = 100000;
public static void main(String[] args) throws InterruptedException {
//1.创建手动锁
Lock lock = new ReentrantLock();
//+10w
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < maxSize; i++) {
//2.加锁
lock.lock();
try {
number++;
}finally {
//3.释放锁
lock.unlock();
}
}
}
});
t1.start();
//-10w
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < maxSize; i++) {
lock.lock();
try {
number--;
}finally {
lock.unlock();
}
}
}
});
t2.start();
//等t1,t2线程执行完
t1.join();
t2.join();
System.out.println("运行结果为:" + number);
}
最后输出0,线程安全
注意事项:
(1)lock 写在 try 之前
(2)一定要记得在 finally 里面进行 unlock()
2. 信号量 Semaphore
作用
用来控制锁的数量
常见方法
Semaphore():构造方法,可以设置信号量个数,以及是否为公平锁
acquire() :尝试获取锁,如果可以正常获取到,则执行后面的业务逻辑,如果获取失败,则阻塞等待
release() :释放锁
代码举例
我们构造4辆车,以及2个停车位,让4辆车进入2个停车位,用到Semaphore信号量
public static void main(String[] args) {
//创建信号量
Semaphore semaphore = new Semaphore(2);
//线程池执行一个任务相当于一辆车进入停车场
ThreadPoolExecutor executor = new ThreadPoolExecutor(10,10,0,
TimeUnit.SECONDS,new LinkedBlockingQueue<>(100));
for (int i = 0; i < 4; i++) {
//创建任务1
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 到达停车场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//试图进入车位
try {
//尝试获取锁
semaphore.acquire();
//当代码执行到此处说明已经获取到了锁
System.out.println(Thread.currentThread().getName() + " 进入车位----");
//车辆停留的时间构建
int num = 1 + new Random().nextInt(5);
try {
Thread.sleep(num * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "离开停车场。。。。");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放锁
semaphore.release();
}
}
});
}
}
可以看到,始终只有2辆车占用停车位,哪个车走了,停车位让出来了,后面的车才能进入
从而就实现了流量控制
3. 计数器 CountDownLatch
作用
计数器是用来保证一组线程同时完成某个任务
常用方法
CountDownLatch():构造方法,参数为n个线程
await() :等待,当线程数量不满足 CountDownLatch 的数量的时候,执行此代码会阻塞等待,直到数量满足之后,再执行 await 之后的代码
countDown():计数器数量-1
实现原理
在 CountDownLatch 里面有一个计数器,每次调用countDown() 方法的时候,计数器数量-1,直到减到 0 之后,就可以执行 await() 之后的代码了
代码实例
设置5个人同时起跑,每到达终点一个人,计数器-1,最后计数器为0时,公布成绩
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
for (int i = 1; i < 6; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始起跑");
try {
Thread.sleep(finalI * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "到达终点");
//计数器-1
latch.countDown();
}
}).start();
}
//阻塞等待
latch.await();
System.out.println("所有人都到达了终点,公布排名");
}
由此可见,当计数器没到0之前,就会阻塞等待,到0后,则执行await后面的代码
缺点
CountDownLatch 计时器的使用是一次性的,当用完一次后,就不能再使用了
4. 循环屏障 CyclicBarrier
作用
也是计时器,不过这个可以解决CountDownLatch的缺点,可以多次进行计数
常用方法
CyclicBarrier(int,Runnable):在int线程计数器为0时,继续执行runnable方法
await():等待,(1)计数器 -1 (2)判断计数器是否为 0,如果为 0 执行之后的代码,如果不为 0 阻塞等待
PS:当计数器为 0 时,首先会执行 await 之后的代码,将计数器重置
代码举例
和上面计数器例子一样,不过这时候10个线程起跑,需要每5个人记录一下时间
public static void main(String[] args) throws InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("执行了CyclicBarrier里面的Runnable");
}
});
for (int i = 1; i < 11; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始起跑");
try {
Thread.sleep(1000 * finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
//
try {
System.out.println(Thread.currentThread().getName() + "等待其他人---");
//计数器-1,判断计数器是否为0
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
// 代码执行到此行,说明已经有一组线程满足条件了
System.out.println(Thread.currentThread().getName() + "执行结束。。。");
}
}).start();
}
}
可以看到此类可以循环的计数,每次计数器为0时,又将计数器置为初始值
CyclicBarrier 和 CountDownLatch 区别
- CountDownLatch 计数器只能使用一次;
- CyclicBarrier 他的计数器可以重复使用;
以上是关于举例详解 java.util.concurrent 并发包 4 种常见类的主要内容,如果未能解决你的问题,请参考以下文章
java.util.concurrent.atomic 包详解
java.util.concurrent BlockingQueue详解
[译]Java Concurrent Atomic Package详解