中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

spring異步service中處理線程數限制詳解

發布時間:2020-10-01 12:34:54 來源:腳本之家 閱讀:246 作者:soft_xiang 欄目:編程語言

情況簡介

spring項目,controller異步調用service的方法,產生大量并發。

具體業務:

前臺同時傳入大量待翻譯的單詞,后臺業務接收單詞,并調用百度翻譯接口翻譯接收單詞并將翻譯結果保存到數據庫,前臺不需要實時返回翻譯結果。

處理方式:

controller接收文本調用service中的異步方法,將單詞先保存到隊列中,再啟動2個新線程,從緩存隊列中取單詞,并調用百度翻譯接口獲取翻譯結果并將翻譯結果保存到數據庫。

本文主要知識點:

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

代碼如下:

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

 private final BlockingQueue<Lgts> que = new LinkedBlockingQueue<>();// 待翻譯的隊列
 private final AtomicInteger threadCnt = new AtomicInteger(0);// 當前翻譯中的線程數
 private final Vector<String> existsKey = new Vector<>();// 保存已入隊列的數據
 private final int maxThreadCnt = 2;// 允許同時執行的翻譯線程數
 private static final int NUM_OF_EVERY_TIME = 50;// 每次提交的翻譯條數
 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() + "]加入隊列結果[" + result
   // + "],隊列中數據總個數:" + que.size());
   return result;
  }
  return false;
 }

 @Autowired
 private LgtsService lgtsService;

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

    while (continueFlag) {// 隊列不為空時執行
     if (Objects.isNull(que.peek())) {
      try {
       if (continueCnt > maxContinueCnt) {
        // 連續休眠次數達到最大連續休眠次數,當前線程將銷毀。
        continueFlag = false;
        continue;
       }
       // 隊列為空,準備休眠
       Thread.sleep(sleepTime);
       continueCnt++;
       continue;
      } catch (InterruptedException e) {
       // 休眠失敗,無需處理
       e.printStackTrace();
      }
     }
     continueCnt = 0;// 重置連續休眠次數為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,以免下面翻譯失敗時再次加入隊列時,加入不進去
      String key = lgts.getGco() + "-" + lgts.getCode();
      existsKey.remove(key);

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

   /**
    * 如果是最后一個線程,清空隊列和existsKey中的數據
    */
   private void distory() {
    if (0 == threadCnt.get()) {
     // 最后一個線程退出時,執行清理操作
     existsKey.clear();
     que.clear();
    }
   }
  };
  thr.setDaemon(true);// 守護線程,如果主線程執行完畢,則此線程會自動銷毀
  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 翻譯結果
  */
 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相關參數為空!");
   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返回結果異常!", 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接口相關參數為空!");
   return params;
  }

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

  params.put("appid", appidStr);

  // 隨機數
  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();
 }

 /**
  * 對輸入的字符串進行URL編碼, 即轉換為%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;
 }
}

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

凉城县| 宾川县| 格尔木市| 托克逊县| 宁强县| 乐清市| 楚雄市| 开封市| 台北市| 濉溪县| 全州县| 新竹县| 正定县| 平山县| 南投县| 宝应县| 高淳县| 文登市| 即墨市| 武宁县| 宁都县| 抚顺县| 秀山| 大理市| 东明县| 建昌县| 资阳市| 远安县| 宁武县| 甘洛县| 五大连池市| 镇雄县| 中山市| 陇南市| 肥东县| 祥云县| 彩票| 南昌市| 宝鸡市| 兴化市| 双鸭山市|