异步变为同步

168 阅读2分钟

使用场景:

app--(http)-->netty--(tcp)-->device

场景说明:

当app需要控制一个设备的开关,不仅物理设备需要有响应,同时需要给app响应开关的控制结果,这种情况下,如果在netty通过tcp发送给device的时候得到响应,那么就会是一个新的线程处理这个响应,跟之前发送的线程无关,这样的话可能不知道怎么响应给app

解决方案

可以使用juc中的CountDownLatch解决

代码实现

同步请求类

package com.ywb.syc2asyc;  
  
import java.util.Objects;  
import java.util.concurrent.CountDownLatch;  
import java.util.concurrent.TimeUnit;  
  
/**  
* @Author ywb  
* @Date 2023/10/15 14:47  
*/  
public class ScyRqe {  
  
    /**  
    * juc中的阻塞工具  
    */  
    private final CountDownLatch countDownLatch = new CountDownLatch(1);  
    /**  
    * device 响应体  
    */  
    private Object responseBoyd;  
    /**  
    * 当前ScyRqe对于的key  
    */  
    private String key;  

    public ScyRqe await(int timeOut, TimeUnit timeUnit) {  
         try {  
            //在这里阻塞等待  
            boolean waiteNoTimeOut = countDownLatch.await(timeOut, timeUnit);  
            //超时了,或者说有响应结果了(这里的其实还可以直接将自己从map中清理)  
            ScyRqe scyRqe = SycReqManager.obtainScyRqe(key);  
            //未超时就返回自己 , 否则返回空  
            return waiteNoTimeOut ? scyRqe : null;  
        } catch (InterruptedException e) {  
            throw new RuntimeException(e);  
        }  
    }  

    public void putResponseBody(Object responseBoyd) {  
        this.responseBoyd = responseBoyd;  
        //打断阻塞  
        countDownLatch.countDown();  
    }  

    public Object getResponseBoyd() {  
        return responseBoyd;  
    }  

}

同步请求类管理器

package com.ywb.syc2asyc;  
  
import java.util.concurrent.ConcurrentHashMap;  
  
/**  
* @Author ywb  
* @Date 2023/10/15 14:46  
*/  
public class SycReqManager {  
  
    /**  
    * 管理ScyRqe  
    */  
    private final static ConcurrentHashMap<String, ScyRqe> ScyRqeList = new ConcurrentHashMap<>();  
  
    /**  
    * 存进去  
    */  
    public static ScyRqe contributeScyRqe(String key, ScyRqe scyRqe) {  
            return ScyRqeList.put(key, scyRqe);  
    }  
  
    /**  
    * 取出来  
    */  
    public static ScyRqe obtainScyRqe(String key) {  
            return ScyRqeList.remove(key);  
    }  
  
}

模拟netty请求响应

package com.ywb.syc2asyc;  
  
import java.util.concurrent.TimeUnit;  
  
/**  
* @Author ywb  
* @Date 2023/10/15 14:56  
* 模拟请求响应  
*/  
public class ImitateRequest {  
  
    /**  
    * 模拟从netty发送数据给device  
    *  
    * @return 响应给app的数据  
    */  
    public Object request() {  
            final String key = "请求id 或者 (设备类型+设备id+控制命令) 等";  
            //创建同步请求  
            ScyRqe scyRqe = new ScyRqe();  
            //存入管理器中,会返回同一个key的value,如果不为空,代表刚才在超时时间内请求过了,那就不管,否则存进去并且发送给device  
            ScyRqe scyRqeOld = SycReqManager.contributeScyRqe(key, scyRqe);  
            if (scyRqeOld == null) {  
                //发送消息 send2Device();  
                //阻塞等待结果  
                ScyRqe scyRqeResponse = scyRqe.await(1, TimeUnit.SECONDS);  
                if (scyRqeResponse == null) {  
                    //超时  
                    return "超时";  
                }  
                return scyRqeResponse.getResponseBoyd();  
            }  
            return "短时间内请求过了,不能多次请求";  
    }  
  
    /**  
    * 模拟从device响应结果给netty  
    */  
    public void response(Object deviceResponseBody) {  
        //解析 deviceResponseBody 获取到 key  
        final String key = "请求id 或者 (设备类型+设备id+控制命令) 等";  
        //从管理器中获取同步请求  
        ScyRqe scyRqe = SycReqManager.obtainScyRqe(key);  
        //将结果放进去,这时候阻塞就会被打断,上面的scyRqe.await(1, TimeUnit.SECONDS)被放行  
        scyRqe.putResponseBody(deviceResponseBody);  
    }  

}