详解Java中CountDownLatch异步转同步工具类

 更新时间:2021年06月30日 10:38:27   作者:lingzhi_ying  
今天给大家带来的是关于Java的相关知识,文章围绕着CountDownLatch异步转同步工具类展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下

使用场景

由于公司业务需求,需要对接socket、MQTT等消息队列。
众所周知 socket 是双向通信,socket的回复是人为定义的,客户端推送消息给服务端,服务端的回复是两条线。无法像http请求有回复。
下发指令给硬件时,需要校验此次数据下发是否成功。
用户体验而言,点击按钮就要知道此次的下发成功或失败。

在这里插入图片描述

如上图模型,

第一种方案使用Tread.sleep
优点:占用资源小,放弃当前cpu资源
缺点: 回复速度快,休眠时间过长,仍然需要等待休眠结束才能返回,响应速度是固定的,无法及时响应第二种方案使用CountDownLatch

package com.lzy.demo.delay;

import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CountDownLatchPool {

    //countDonw池
    private final static Map<Integer, CountDownLatch> countDownLatchMap = new ConcurrentHashMap<>();
    //延迟队列
    private final static DelayQueue<MessageDelayQueueUtil> delayQueue = new DelayQueue<>();

    private volatile static boolean flag =false;
    //单线程池
    private final static ExecutorService t = new ThreadPoolExecutor(1, 1,
        0L, TimeUnit.MILLISECONDS,
        new ArrayBlockingQueue<>(1));

    public static void addCountDownLatch(Integer messageId) {
        CountDownLatch countDownLatch = countDownLatchMap.putIfAbsent(messageId,new CountDownLatch(1) );
        if(countDownLatch == null){
            countDownLatch = countDownLatchMap.get(messageId);
        }
        try {
            addDelayQueue(messageId);
            countDownLatch.await(3L, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("阻塞等待结束~~~~~~");
    }

    public static void removeCountDownLatch(Integer messageId){
        CountDownLatch countDownLatch = countDownLatchMap.get(messageId);
        if(countDownLatch == null)
            return;
        countDownLatch.countDown();
        countDownLatchMap.remove(messageId);
        System.out.println("清除Map数据"+countDownLatchMap);
    }

    private static void addDelayQueue(Integer messageId){
        delayQueue.add(new MessageDelayQueueUtil(messageId));
        clearMessageId();
    }

    private static void clearMessageId(){
        synchronized (CountDownLatchPool.class){
            if(flag){
                return;
            }
            flag = true;
        }
        t.execute(()->{
            while (delayQueue.size() > 0){
                System.out.println("进入线程并开始执行");
                try {
                    MessageDelayQueueUtil take = delayQueue.take();
                    Integer messageId1 = take.getMessageId();
                    removeCountDownLatch(messageId1);
                    System.out.println("清除队列数据"+messageId1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            flag = false;
            System.out.println("结束end----");
        });
    }

    public static void main(String[] args) throws InterruptedException {
        /*
        测试超时清空map
        new Thread(()->addCountDownLatch(1)).start();
        new Thread(()->addCountDownLatch(2)).start();
        new Thread(()->addCountDownLatch(3)).start();
        */
        //提前创建线程,清空countdown
        new Thread(()->{
            try {
                Thread.sleep(500L);
                removeCountDownLatch(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //开始阻塞
        addCountDownLatch(1);
    	//通过调整上面的sleep我们发现阻塞市场取决于countDownLatch.countDown()执行时间
    	System.out.println("阻塞结束----");
    }
}
class MessageDelayQueueUtil implements Delayed {

    private Integer messageId;
    private long avaibleTime;

    public Integer getMessageId() {
        return messageId;
    }

    public void setMessageId(Integer messageId) {
        this.messageId = messageId;
    }

    public long getAvaibleTime() {
        return avaibleTime;
    }

    public void setAvaibleTime(long avaibleTime) {
        this.avaibleTime = avaibleTime;
    }

    public MessageDelayQueueUtil(Integer messageId){
        this.messageId = messageId;
        //avaibleTime = 当前时间+ delayTime
        //重试3次,每次3秒+1秒的延迟
        this.avaibleTime=3000*3+1000 + System.currentTimeMillis();
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long diffTime= avaibleTime- System.currentTimeMillis();
        return unit.convert(diffTime,TimeUnit.MILLISECONDS);
    }

    @Override
    public int compareTo(Delayed o) {
        //compareTo用在DelayedUser的排序
        return (int)(this.avaibleTime - ((MessageDelayQueueUtil) o).getAvaibleTime());
    }
}

由于socket并不确定每次都会有数据返回,所以map的数据会越来越大,最终导致内存溢出
需定时清除map内的无效数据。
可以使用DelayedQuene延迟队列来处理,相当于给对象添加一个过期时间

使用方法 addCountDownLatch 等待消息,异步回调消息清空removeCountDownLatch

到此这篇关于详解Java中CountDownLatch异步转同步工具类的文章就介绍到这了,更多相关CountDownLatch异步转同步工具类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java请求调用参数格式为form-data类型的接口代码示例

    Java请求调用参数格式为form-data类型的接口代码示例

    这篇文章主要给大家介绍了关于Java请求调用参数格式为form-data类型的接口的相关资料,文中给出了详细的代码示例,对大家的学习或者工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • 亲身体验Intellij Idea从卡顿到顺畅全过程

    亲身体验Intellij Idea从卡顿到顺畅全过程

    这篇文章主要介绍了亲身体验Intellij Idea从卡顿到顺畅全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • Spring Boot两种全局配置和两种注解的操作方法

    Spring Boot两种全局配置和两种注解的操作方法

    Spring Boot使用一个application.properties或者application.yaml的文件作为全局配置文件,本文重点给大家介绍Spring Boot两种全局配置和两种注解的配置方法,感兴趣的朋友一起看看吧
    2021-06-06
  • Java优秀测试框架TestNG详解

    Java优秀测试框架TestNG详解

    这篇文章主要为大家详细介绍了Java优秀测试框架TestNG,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • 详解APP微信支付(java后台_统一下单和回调)

    详解APP微信支付(java后台_统一下单和回调)

    这篇文章主要介绍了APP微信支付(java后台_统一下单和回调),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • Java实现在线语音识别

    Java实现在线语音识别

    这篇文章主要为大家详细介绍了Java实现在线语音识别功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-08-08
  • Java排序方法sort用法详解

    Java排序方法sort用法详解

    这篇文章主要为大家详细介绍了Java排序方法sort用法,对数组、集合的排序方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Java必备知识之位运算及常见进制解读

    Java必备知识之位运算及常见进制解读

    从现代计算机中所有的数据二进制的形式存储在设备中。即 0、1 两种状态,计算机对二进制数据进行的运算(+、-、*、/)都是叫位运算,即将符号位共同参与运算的运算
    2021-10-10
  • springboot CompletableFuture并行计算及使用方法

    springboot CompletableFuture并行计算及使用方法

    CompletableFuture基于 Future 和 CompletionStage 接口,利用线程池、回调函数、异常处理、组合操作等机制,提供了强大而灵活的异步编程功能,这篇文章主要介绍了springboot CompletableFuture并行计算及使用方法,需要的朋友可以参考下
    2024-05-05
  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    SpringBoot + Spring Security 基本使用及个性化登录配置详解

    这篇文章主要介绍了SpringBoot + Spring Security 基本使用及个性化登录配置详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05

最新评论