欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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ù):

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

處理方式:

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

本文主要知識(shí)點(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) {
   // 已啟動(dòng)的線程大于設(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í)行完畢,則此線程會(huì)自動(dòng)銷毀
  thr.setName("baidufanyi-" + RandomUtils.nextInt(1000, 9999));
  thr.start();// 啟動(dòng)插入線程
 }

 /**
  * 百度翻譯
  * 
  * @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)文章

  • springboot連接redis并動(dòng)態(tài)切換database的實(shí)現(xiàn)方法

    springboot連接redis并動(dòng)態(tài)切換database的實(shí)現(xiàn)方法

    這篇文章主要介紹了springboot連接redis并動(dòng)態(tài)切換database,本文主為通過修改ConnectionFactory從而達(dá)到動(dòng)態(tài)切換database的效果,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • java拋出異常后,后續(xù)代碼是否繼續(xù)執(zhí)行詳解

    java拋出異常后,后續(xù)代碼是否繼續(xù)執(zhí)行詳解

    這篇文章主要給大家介紹了關(guān)于java拋出異常后,后續(xù)代碼是否繼續(xù)執(zhí)行詳?shù)南嚓P(guān)資料,在Java編程中,異常是當(dāng)程序執(zhí)行時(shí)遇到問題時(shí)拋出的一種特殊情況,需要的朋友可以參考下
    2023-07-07
  • 詳細(xì)分析Java內(nèi)存模型

    詳細(xì)分析Java內(nèi)存模型

    Java虛擬機(jī)規(guī)范中定義了Java內(nèi)存模型(Java Memory Model,JMM),用于屏蔽掉各種硬件和操作系統(tǒng)的內(nèi)存訪問差異,以實(shí)現(xiàn)讓Java程序在各種平臺(tái)下都能達(dá)到一致的并發(fā)效果,JMM規(guī)范了Java虛擬機(jī)與計(jì)算機(jī)內(nèi)存是如何協(xié)同工作的,以及在必須時(shí)如何同步的訪問共享變量
    2021-06-06
  • 深入理解Java中包的定義與使用

    深入理解Java中包的定義與使用

    在開發(fā)過程中,會(huì)定義很多類,為了避免相同類名稱出現(xiàn)而發(fā)生覆蓋的情況,把所有java程序保存在各自的目錄里面,而該目錄就是包。包的本質(zhì)實(shí)際上就是一個(gè)文件夾。本文將給大家詳細(xì)的介紹,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值
    2021-09-09
  • 全面了解java中的異常處理

    全面了解java中的異常處理

    java中的異常處理是java語言中的一大重要特性,它分離了接收和處理錯(cuò)誤代碼。這篇文章非常詳細(xì)的講解了java中的這一特性,感興趣的小伙伴一起來學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Spring+SpringMVC+MyBatis整合詳細(xì)教程(SSM)

    Spring+SpringMVC+MyBatis整合詳細(xì)教程(SSM)

    Spring是一個(gè)開源框架,Spring是于2003 年興起的一個(gè)輕量級的Java 開發(fā)框架。這篇文章主要介紹了Spring+SpringMVC+MyBatis整合詳細(xì)教程(SSM),需要的朋友可以參考下
    2017-10-10
  • java猜數(shù)字小游戲案例

    java猜數(shù)字小游戲案例

    這篇文章主要為大家詳細(xì)介紹了java猜數(shù)字小游戲案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • SpringBoot整合MybatisPlus的基本應(yīng)用詳解

    SpringBoot整合MybatisPlus的基本應(yīng)用詳解

    MyBatis-Plus (簡稱 MP)是一個(gè) MyBatis的增強(qiáng)工具,在 MyBatis 的基礎(chǔ)上只做增強(qiáng)不做改變,為 簡化開發(fā)、提高效率而生,本文將給大家介紹一下SpringBoot整合MybatisPlus的基本應(yīng)用,需要的朋友可以參考下
    2024-05-05
  • springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過程

    springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過程

    這篇文章主要給大家介紹了關(guān)于springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過程,Spring Boot應(yīng)用中可以配置多個(gè)數(shù)據(jù)源,并根據(jù)注解靈活指定當(dāng)前使用的數(shù)據(jù)源,需要的朋友可以參考下
    2023-08-08
  • 深入了解Java線程池:從設(shè)計(jì)思想到源碼解讀

    深入了解Java線程池:從設(shè)計(jì)思想到源碼解讀

    這篇文章將從設(shè)計(jì)思想到源碼解讀,帶大家深入了解Java的線程池,文中的示例代碼講解詳細(xì),對我們的學(xué)習(xí)或工作有一定的幫助,需要的可以參考一下
    2021-12-12

最新評論