关于JUC并发包,你不得不知道的知识

tech2023-02-10  91

「AQS」是AbstractQueuedSynchronizer的简称,它提供了一种等待唤醒的机制。底层核数据结构是双端队列。

采用自旋 + LockSupport + CAS来实现这种等待唤醒。在java的「JUC」并发包下很多类都是基于「AQS」实现的。下面我们来介绍几种并发类。

Semaphore

俗称「信号量」,用于控制在同一时间内共享资源被多少线程共享。可以作为流浪控制。

 

使用案例

/**  * Description:信号量机制  *  * @author Lvshen  * @version 1.0  * @date: 2020/3/21 20:34  * @since JDK 1.8  */ public class SemaphoreDemo {     public static void main(String[] args) {         SemaphoreDemo semaphoreDemo = new SemaphoreDemo();         int count = 9;    //数量         //循环屏障         CyclicBarrier cyclicBarrier = new CyclicBarrier(count);         Semaphore semaphore = new Semaphore(5);//限制请求数量         for (int i = 0; i < count; i++) {             String vipNo = "vip-00" + i;             new Thread(() -> {                 try {                     cyclicBarrier.await();                     //semaphore.acquire();//获取令牌                     boolean tryAcquire = semaphore.tryAcquire(3000L, TimeUnit.MILLISECONDS);                     if (!tryAcquire) {                         System.out.println("获取令牌失败:" + vipNo);                     }                     //执行操作逻辑                     System.out.println("当前线程:" + Thread.currentThread().getName());                     semaphoreDemo.service(vipNo);                 } catch (Exception e) {                     e.printStackTrace();                 } finally {                     semaphore.release();                 }             }).start();         }     }     // 限流控制5个线程同时访问     public void service(String vipNo) throws InterruptedException {         System.out.println("楼上出来迎接贵宾一位,贵宾编号" + vipNo + ",...");         Thread.sleep(new Random().nextInt(3000));         System.out.println("欢送贵宾出门,贵宾编号" + vipNo);     } }

如上代码,我用Semaphore来控制semaphoreDemo.service(vipNo)的请求数量。如果并发时间请求数量超过5,弹出"获取令牌失败..."。

CyclicBarrier

俗称「栅栏」机制,用于控制线程在同一时间点并发请求。

英语好的可以看看官方解释

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point . The barrier is called cyclic because it can be re-used after the waiting threads are released.

使用案例

public class ConcurrentTestDemo {     public static void main(String[] args) {         //并发数         int currency = 20;         //循环屏障         CyclicBarrier cyclicBarrier = new CyclicBarrier(currency);         for (int i = 0; i < currency; i++) {             new Thread(() -> {                 OrderServiceImplWithDisLock orderService = new OrderServiceImplWithDisLock();                 System.out.println(Thread.currentThread().getName() + "====start====");                 //等待一起出发                 try {                     cyclicBarrier.await();                 } catch (InterruptedException | BrokenBarrierException e) {                     e.printStackTrace();                 }                 orderService.createOrder();             }).start();         }     } }

当线程数满20时,才会并发执行orderService.createOrder()。

CountDownLatch

俗称倒计数器,允许一个或多个线程一直等待,直到一组在其他线程执行的操作全部完成。当一个线程调用await方法时,就会阻塞当前线程。每当有线程调用一次 countDown 方法时,计数就会减 1。当 count 的值等于 0 的时候,被阻塞的线程才会继续运行。

源码解释如下

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately. This is a one-shot phenomenon – the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.

A CountDownLatch is a versatile synchronization tool and can be used for a number of purposes. A CountDownLatch initialized with a count of one serves as a simple on/off latch, or gate: all threads invoking await wait at the gate until it is opened by a thread invoking countDown(). A CountDownLatch initialized to N can be used to make one thread wait until N threads have completed some action, or some action has been completed N times.

使用案例

@Bean(name = "zkClient")     public ZooKeeper zkClient(){         ZooKeeper zooKeeper=null;         try {             final CountDownLatch countDownLatch = new CountDownLatch(1);             //连接成功后,会回调watcher监听,此连接操作是异步的,执行完new语句后,直接调用后续代码             //  可指定多台服务地址 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183             zooKeeper = new ZooKeeper(connectString, timeout, new Watcher() {                 @Override                 public void process(WatchedEvent event) {                     if(Event.KeeperState.SyncConnected==event.getState()){                         //如果收到了服务端的响应事件,连接成功                         countDownLatch.countDown();                     }                 }             });             countDownLatch.await();             log.info("【初始化ZooKeeper连接状态....】={}",zooKeeper.getState());         }catch (Exception e){             log.error("初始化ZooKeeper连接异常....】={}",e);         }         return  zooKeeper;     } }

如上代码,可用于当收到服务器响应,才返回zookeeper。

总结

Semaphore比较好理解,需要拿到许可才能执行,并可以选择公平和非公平模式。

对于CyclicBarrier和CountDownLatch我总结一下几点:

CountDownLatch 是一个线程等待其他线程,CyclicBarrier是多个线程互相等待。

CountDownLatch 是一次性的, CyclicBarrier  可以循环利用。

这篇文章只是初步介绍「AQS」并发类的一些使用场景,后续我会讲解它们的底层原理,敬请期待。

往期推荐

我写出这样干净的代码,老板直夸我

云南丽江旅游攻略

使用ThreadLocal怕内存泄漏?

Java进阶之路思维导图

程序员必看书籍推荐

3万字的Java后端面试总结(附PDF)

扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料

1.回复"java" 获取java电子书; 2.回复"python"获取python电子书; 3.回复"算法"获取算法电子书; 4.回复"大数据"获取大数据电子书; 5.回复"spring"获取SpringBoot的学习视频。 6.回复"面试"获取一线大厂面试资料 7.回复"进阶之路"获取Java进阶之路的思维导图 8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版) 9.回复"总结"获取Java后端面试经验总结PDF版 10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF) 11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)

另:点击【我的福利】有更多惊喜哦。

最新回复(0)