这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战
前言
每天积累一点点,积跬步至千里。今天学习
JUC包中的CyclicBarrier、ReentrantLock。
CyclicBarrier
正如字面翻译的意思一样,
CyclicBarrier循环屏障,CyclicBarrier也是JUC并发包下得一个工具类,它的作用就类似于一道闸门,允许一组线程相互等待,直到所有的线程都到了闸门前(又称为公共屏障点),再一起放行。这时保证同一组的所有的线程同一时间执行。**注意:**与CountDownLatch不同的是该barrier在释放等待线程后可以重用,形象点就是这道闸打开后这一组放行完会立即关上,阻拦住第二组的线程。所以这就是为什么叫 循环(Cyclic)屏障(Barrier)
代码示例
public class CyclicBarrierSample {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 1 ; i<=20 ; i++) {
final int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.execute(new Runnable() {
@Override
public void run() {
go();
}
});
}
executorService.shutdown();
}
private static void go(){
System.out.println(Thread.currentThread().getName() + ":准备就绪" );
try {
cyclicBarrier.await();
System.out.println(Thread.currentThread().getName() + ":开始运行");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
cyclicBarrier.await()设置屏障点,当累计5个线程都准备好后,才运行后面的代码- 打印结果可以看出五个一组,分四次执行完毕。
应用场景
同一时间点,多个线程同时执行
cpu性能评测- 秒杀场景
- 抢票机器人
ReentrantLock
ReentrantLock重入锁,当任意的线程在获得到锁之后,再次获取该锁而不会被该锁阻塞。ReentrantLock设计的目标是用来替代synchronized关键字
ReentrantLock与synchronized的区别
| 特征 | synchronized(推荐) | reentrantLock |
|---|---|---|
| 底层原理 | JVM实现 | JDK实现 |
| 性能区别 | 低 -> 高(JDK1.5+) | 高 |
| 锁的释放 | 自动释放(编译器保证) | 手动释放(finally) |
| 编码程度 | 简单 | 复杂 |
| 锁的粒度 | 读写不区分 | 读锁、写锁 |
| 高级功能 | 无 | 公平锁、非公平锁唤醒、Condition分组唤醒、中断等待锁 |
代码示例
public class ReentrantLockSample {
public static int users = 100;//同时模拟的并发访问用户数量
public static int downTotal = 50000; //用户下载的真实总数
public static int count = 0;//计数器
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
//调度器,JDK1.5后提供的concurrent包对于并发的支持
ExecutorService executorService = Executors.newCachedThreadPool();
//信号量,用于模拟并发的人数
final Semaphore semaphore = new Semaphore(users);
for (int i = 0; i < downTotal; i++) {
executorService.execute(() -> {
//通过多线程模拟N个用户并发访问并下载
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
e.printStackTrace();
}
});
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();//关闭调度服务
System.out.println("下载总数:" + count);
}
//线程不安全
public static void add() {
lock.lock();//上锁
try {
count++;
} finally {
lock.unlock(); //解锁,一定要放在finally里面否则会出现死锁
}
}
}
在现实开发中一般不推荐使用这种方式,首先每次都需要手动的去释放资源,稍有疏忽就会造成严重后果,其次在
JDK1.5后synchronized的性能也不差。