最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring異步service中處理線程數(shù)限制詳解

 更新時(shí)間:2019年09月05日 16:31:56   作者:soft_xiang  
這篇文章主要給大家介紹了關(guān)于spring異步service中處理線程數(shù)限制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧

情況簡介

spring項(xiàng)目,controller異步調(diào)用service的方法,產(chǎn)生大量并發(fā)。

具體業(yè)務(wù):

前臺同時(shí)傳入大量待翻譯的單詞,后臺業(yè)務(wù)接收單詞,并調(diào)用百度翻譯接口翻譯接收單詞并將翻譯結(jié)果保存到數(shù)據(jù)庫,前臺不需要實(shí)時(shí)返回翻譯結(jié)果。

處理方式:

controller接收文本調(diào)用service中的異步方法,將單詞先保存到隊(duì)列中,再啟動2個(gè)新線程,從緩存隊(duì)列中取單詞,并調(diào)用百度翻譯接口獲取翻譯結(jié)果并將翻譯結(jié)果保存到數(shù)據(jù)庫。

本文主要知識點(diǎn):

多線程同時(shí)(異步)調(diào)用方法后,開啟新線程,并限制線程數(shù)量。

代碼如下:

@Service
public class LgtsAsyncServiceImpl {
 /** logger日志. */
 public static final Logger LOGGER = Logger.getLogger(LgtsAsyncServiceImpl2.class);

 private final BlockingQueue<Lgts> que = new LinkedBlockingQueue<>();// 待翻譯的隊(duì)列
 private final AtomicInteger threadCnt = new AtomicInteger(0);// 當(dāng)前翻譯中的線程數(shù)
 private final Vector<String> existsKey = new Vector<>();// 保存已入隊(duì)列的數(shù)據(jù)
 private final int maxThreadCnt = 2;// 允許同時(shí)執(zhí)行的翻譯線程數(shù)
 private static final int NUM_OF_EVERY_TIME = 50;// 每次提交的翻譯條數(shù)
 private static final String translationFrom = "zh";

 @Async
 public void saveAsync(Lgts t) {
  if (Objects.isNull(t) || StringUtils.isAnyBlank(t.getGco(), t.getCode())) {
   return;
  }
  offer(t);
  save();
  return;
 }

 private boolean offer(Lgts t) {
  String key = t.getGco() + "-" + t.getCode();
  if (!existsKey.contains(key)) {
   existsKey.add(key);
   boolean result = que.offer(t);
   // LOGGER.trace("待翻譯文字[" + t.getGco() + ":" + t.getCode() + "]加入隊(duì)列結(jié)果[" + result
   // + "],隊(duì)列中數(shù)據(jù)總個(gè)數(shù):" + que.size());
   return result;
  }
  return false;
 }

 @Autowired
 private LgtsService lgtsService;

 private void save() {
  int cnt = threadCnt.incrementAndGet();// 當(dāng)前線程數(shù)+1
  if (cnt > maxThreadCnt) {
   // 已啟動的線程大于設(shè)置的最大線程數(shù)直接丟棄
   threadCnt.decrementAndGet();// +1的線程數(shù)再-回去
   return;
  }
  GwallUser user = UserUtils.getUser();
  Thread thr = new Thread() {
   public void run() {
    long sleepTime = 30000l;
    UserUtils.setUser(user);
    boolean continueFlag = true;
    int maxContinueCnt = 5;// 最大連續(xù)休眠次數(shù),連續(xù)休眠次數(shù)超過最大休眠次數(shù)后,while循環(huán)退出,當(dāng)前線程銷毀
    int continueCnt = 0;// 連續(xù)休眠次數(shù)

    while (continueFlag) {// 隊(duì)列不為空時(shí)執(zhí)行
     if (Objects.isNull(que.peek())) {
      try {
       if (continueCnt > maxContinueCnt) {
        // 連續(xù)休眠次數(shù)達(dá)到最大連續(xù)休眠次數(shù),當(dāng)前線程將銷毀。
        continueFlag = false;
        continue;
       }
       // 隊(duì)列為空,準(zhǔn)備休眠
       Thread.sleep(sleepTime);
       continueCnt++;
       continue;
      } catch (InterruptedException e) {
       // 休眠失敗,無需處理
       e.printStackTrace();
      }
     }
     continueCnt = 0;// 重置連續(xù)休眠次數(shù)為0

     List<Lgts> params = new ArrayList<>();
     int totalCnt = que.size();
     que.drainTo(params, NUM_OF_EVERY_TIME);
     StringBuilder utf8q = new StringBuilder();
     String code = "";
     List<Lgts> needRemove = new ArrayList<>();
     for (Lgts lgts : params) {
      if (StringUtils.isAnyBlank(code)) {
       code = lgts.getCode();
      }
      // 移除existsKey中保存的key,以免下面翻譯失敗時(shí)再次加入隊(duì)列時(shí),加入不進(jìn)去
      String key = lgts.getGco() + "-" + lgts.getCode();
      existsKey.remove(key);

      if (!code.equalsIgnoreCase(lgts.getCode())) {// 要翻譯的目標(biāo)語言與當(dāng)前列表中的第一個(gè)不一致
       offer(lgts);// 重新將待翻譯的語言放回隊(duì)列
       needRemove.add(lgts);
       continue;
      }
      utf8q.append(lgts.getGco()).append("\n");
     }
     params.removeAll(needRemove);
     LOGGER.debug("隊(duì)列中共" + totalCnt + " 個(gè),獲取" + params.size() + " 個(gè)符合條件的待翻譯內(nèi)容,編碼:" + code);
     String to = "en";
     if (StringUtils.isAnyBlank(utf8q, to)) {
      LOGGER.warn("調(diào)用翻譯出錯(cuò),未找到[" + code + "]對應(yīng)的百度編碼。");
      continue;
     }
     Map<String, String> result = getBaiduTranslation(utf8q.toString(), translationFrom, to);
     if (Objects.isNull(result) || result.isEmpty()) {// 把沒有獲取到翻譯結(jié)果的重新放回隊(duì)列
      for (Lgts lgts : params) {
       offer(lgts);
      }
      LOGGER.debug("本次翻譯結(jié)果為空。");
      continue;
     }
     int sucessCnt = 0, ignoreCnt = 0;
     for (Lgts lgts : params) {
      lgts.setBdcode(to);
      String gna = result.get(lgts.getGco());
      if (StringUtils.isAnyBlank(gna)) {
       offer(lgts);// 重新將待翻譯的語言放回隊(duì)列
       continue;
      }
      lgts.setStat(1);
      lgts.setGna(gna);
      int saveResult = lgtsService.saveIgnore(lgts);
      if (0 == saveResult) {
       ignoreCnt++;
      } else {
       sucessCnt++;
      }
     }
     LOGGER.debug("待翻譯個(gè)數(shù):" + params.size() + ",翻譯成功個(gè)數(shù):" + sucessCnt + ",已存在并忽略個(gè)數(shù):" + ignoreCnt);
    }
    threadCnt.decrementAndGet();// 運(yùn)行中的線程數(shù)-1
    distory();// 清理數(shù)據(jù),必須放在方法最后,否則distory中的判斷需要修改
   }

   /**
    * 如果是最后一個(gè)線程,清空隊(duì)列和existsKey中的數(shù)據(jù)
    */
   private void distory() {
    if (0 == threadCnt.get()) {
     // 最后一個(gè)線程退出時(shí),執(zhí)行清理操作
     existsKey.clear();
     que.clear();
    }
   }
  };
  thr.setDaemon(true);// 守護(hù)線程,如果主線程執(zhí)行完畢,則此線程會自動銷毀
  thr.setName("baidufanyi-" + RandomUtils.nextInt(1000, 9999));
  thr.start();// 啟動插入線程
 }

 /**
  * 百度翻譯
  * 
  * @param utf8q
  *   待翻譯的字符串,需要utf8格式的
  * @param from
  *   百度翻譯語言列表中的代碼
  *   參見:http://api.fanyi.baidu.com/api/trans/product/apidoc#languageList
  * @param to
  *   百度翻譯語言列表中的代碼
  *   參見:http://api.fanyi.baidu.com/api/trans/product/apidoc#languageList
  * @return 翻譯結(jié)果
  */
 private Map<String, String> getBaiduTranslation(String utf8q, String from, String to) {
  Map<String, String> result = new HashMap<>();
  String baiduurlStr = "http://api.fanyi.baidu.com/api/trans/vip/translate";
  if (StringUtils.isAnyBlank(baiduurlStr)) {
   LOGGER.warn("百度翻譯API接口URL相關(guān)參數(shù)為空!");
   return result;
  }
  Map<String, String> params = buildParams(utf8q, from, to);
  if (params.isEmpty()) {
   return result;
  }

  String sendUrl = getUrlWithQueryString(baiduurlStr, params);
  try {
   HttpClient httpClient = new HttpClient();
   httpClient.setMethod("GET");
   String remoteResult = httpClient.pub(sendUrl, "");
   result = convertRemote(remoteResult);
  } catch (Exception e) {
   LOGGER.info("百度翻譯API返回結(jié)果異常!", e);
  }
  return result;
 }

 private Map<String, String> convertRemote(String remoteResult) {
  Map<String, String> result = new HashMap<>();
  if (StringUtils.isBlank(remoteResult)) {
   return result;
  }
  JSONObject jsonObject = JSONObject.parseObject(remoteResult);
  JSONArray trans_result = jsonObject.getJSONArray("trans_result");
  if (Objects.isNull(trans_result) || trans_result.isEmpty()) {
   return result;
  }
  for (Object object : trans_result) {
   JSONObject trans = (JSONObject) object;
   result.put(trans.getString("src"), trans.getString("dst"));
  }
  return result;
 }

 private Map<String, String> buildParams(String utf8q, String from, String to) {
  if (StringUtils.isBlank(from)) {
   from = "auto";
  }
  Map<String, String> params = new HashMap<String, String>();
  String skStr = "sk";
  String appidStr = "appid";
  if (StringUtils.isAnyBlank(skStr, appidStr)) {
   LOGGER.warn("百度翻譯API接口相關(guān)參數(shù)為空!");
   return params;
  }

  params.put("q", utf8q);
  params.put("from", from);
  params.put("to", to);

  params.put("appid", appidStr);

  // 隨機(jī)數(shù)
  String salt = String.valueOf(System.currentTimeMillis());
  params.put("salt", salt);

  // 簽名
  String src = appidStr + utf8q + salt + skStr; // 加密前的原文
  params.put("sign", MD5Util.md5Encrypt(src).toLowerCase());
  return params;
 }

 public static String getUrlWithQueryString(String url, Map<String, String> params) {
  if (params == null) {
   return url;
  }

  StringBuilder builder = new StringBuilder(url);
  if (url.contains("?")) {
   builder.append("&");
  } else {
   builder.append("?");
  }

  int i = 0;
  for (String key : params.keySet()) {
   String value = params.get(key);
   if (value == null) { // 過濾空的key
    continue;
   }

   if (i != 0) {
    builder.append('&');
   }

   builder.append(key);
   builder.append('=');
   builder.append(encode(value));

   i++;
  }

  return builder.toString();
 }

 /**
  * 對輸入的字符串進(jìn)行URL編碼, 即轉(zhuǎn)換為%20這種形式
  * 
  * @param input
  *   原文
  * @return URL編碼. 如果編碼失敗, 則返回原文
  */
 public static String encode(String input) {
  if (input == null) {
   return "";
  }

  try {
   return URLEncoder.encode(input, "utf-8");
  } catch (UnsupportedEncodingException e) {
   e.printStackTrace();
  }

  return input;
 }
}

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • 淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型

    淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型

    這篇文章主要介紹了淺談spring ioc的注入方式及注入不同的數(shù)據(jù)類型,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2017-12-12
  • SpringBoot配置log4j輸出日志的案例講解

    SpringBoot配置log4j輸出日志的案例講解

    這篇文章主要介紹了SpringBoot配置log4j輸出日志的案例講解,springboot框架中默認(rèn)使用logback進(jìn)行日志輸出,當(dāng)然它也可以配置其它的日志框架,需要的朋友可以參考下
    2022-11-11
  • Spring-Validation 后端數(shù)據(jù)校驗(yàn)的實(shí)現(xiàn)

    Spring-Validation 后端數(shù)據(jù)校驗(yàn)的實(shí)現(xiàn)

    這篇文章主要介紹了Spring-Validation 后端數(shù)據(jù)校驗(yàn)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • Java WebService 簡單實(shí)例(附實(shí)例代碼)

    Java WebService 簡單實(shí)例(附實(shí)例代碼)

    本篇文章主要介紹了Java WebService 簡單實(shí)例(附實(shí)例代碼), Web Service 是一種新的web應(yīng)用程序分支,他們是自包含、自描述、模塊化的應(yīng)用,可以發(fā)布、定位、通過web調(diào)用。有興趣的可以了解一下
    2017-01-01
  • JAVA 添加、修改和刪除PDF書簽的示例代碼

    JAVA 添加、修改和刪除PDF書簽的示例代碼

    這篇文章主要介紹了JAVA 添加、修改和刪除PDF書簽的示例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06
  • java多線程數(shù)據(jù)分頁處理實(shí)例講解

    java多線程數(shù)據(jù)分頁處理實(shí)例講解

    在本篇內(nèi)容里小編給大家分享了一篇關(guān)于java多線程數(shù)據(jù)分頁處理實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)下。
    2021-01-01
  • Spring中@EnableScheduling注解的工作原理詳解

    Spring中@EnableScheduling注解的工作原理詳解

    這篇文章主要介紹了Spring中@EnableScheduling注解的工作原理詳解,@EnableScheduling是 Spring Framework 提供的一個(gè)注解,用于啟用Spring的定時(shí)任務(wù)(Scheduling)功能,需要的朋友可以參考下
    2024-01-01
  • Java8 HashMap的實(shí)現(xiàn)原理分析

    Java8 HashMap的實(shí)現(xiàn)原理分析

    Java8之后新增挺多新東西,接下來通過本文給大家介紹Java8 HashMap的實(shí)現(xiàn)原理分析,對java8 hashmap實(shí)現(xiàn)原理相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧
    2016-03-03
  • Java行為型設(shè)計(jì)模式之模板方法詳解

    Java行為型設(shè)計(jì)模式之模板方法詳解

    模板方法的概念:定義了一個(gè)操作中的算法的骨架,而將部分步驟的實(shí)現(xiàn)在子類中完成。模板方法模式使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟
    2022-09-09
  • Java判斷兩個(gè)日期相差天數(shù)的方法

    Java判斷兩個(gè)日期相差天數(shù)的方法

    這篇文章主要介紹了Java判斷兩個(gè)日期相差天數(shù)的方法,以實(shí)例形式對比分析了java進(jìn)行日期換算及對比的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-10-10

最新評論

郑州市| 津南区| 富宁县| 锦州市| 于都县| 习水县| 武隆县| 历史| 息烽县| 二连浩特市| 类乌齐县| 萨嘎县| 潜江市| 乌兰浩特市| 镇康县| 龙州县| 建始县| 务川| 射阳县| 波密县| 沧源| 大厂| 尚志市| 西青区| 义马市| 伊宁市| 新邵县| 安多县| 九台市| 仲巴县| 东台市| 竹山县| 临高县| 东山县| 交口县| 聊城市| 榆林市| 鄂温| 青田县| 杨浦区| 双桥区|