成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

日常工作中很容易犯的幾個并發錯誤

開發 前端
列舉大家平時在工作中最容易犯的幾個并發錯誤,都是在實際項目代碼中看到的鮮活例子,希望對大家有幫助。

 前言

列舉大家平時在工作中很容易犯的幾個并發錯誤,都是在實際項目代碼中看到的鮮活例子,希望對大家有幫助。

[[286297]]

First Blood

線上總是出現:ERROR 1062 (23000) Duplicate entry 'xxx' for key 'yyy',我們來看一下有問題的這段代碼:

  1. UserBindInfo info = selectFromDB(userId); 
  2. if(info == null){ 
  3.     info = new UserBindInfo(userId,deviceId); 
  4.     insertIntoDB(info); 
  5. }else
  6.     info.setDeviceId(deviceId); 
  7.     updateDB(info); 
  8.     } 

在并發情況下,第一步判斷都為空,就會有2個或者多個線程進入插入數據庫操作,這時候就出現了同一個ID插入多次。

正確處理姿勢:

 

  1. insert into UserBindInfo values(#{userId},#{deviceId}) on duplicate key update deviceId=#{deviceId}多次的情況,導致插入失敗。 

一般情況下,可以用insert...on duplicate key update... 解決這個問題。

注意: 如果UserBindInfo表存在主鍵以及一個以上的唯一索引,在并發情況下,使用insert...on duplicate key,可能會產生死鎖(Mysql5.7),可以這樣處理:

 

  1. try{ 
  2.    UserBindInfoMapper.insertIntoDB(userBindInfo); 
  3. }catch(DuplicateKeyException ex){ 
  4.     UserBindInfoMapper.update(userBindInfo); 

Double Kill

小心你的全局變量,如下面這段代碼:

  1.  public class GlobalVariableConcurrentTest { 
  2.    
  3.       private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
  4.   
  5.       public static void main(String[] args) throws InterruptedException { 
  6.          ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000)); 
  7.   
  8.          while (true){ 
  9.              threadPoolExecutor.execute(()->{ 
  10.                 String dateString = sdf.format(new Date()); 
  11.                  try { 
  12.                      Date parseDate = sdf.parse(dateString); 
  13.                     String dateString2 = sdf.format(parseDate); 
  14.                      System.out.println(dateString.equals(dateString2)); 
  15.                  } catch (ParseException e) { 
  16.                      e.printStackTrace(); 
  17.                 } 
  18.              }); 
  19.         } 
  20.   
  21.      } 
  22.   

可以看到有異常拋出

 

 

 

全局變量的SimpleDateFormat,在并發情況下,存在安全性問題,阿里Java規約明確要求謹慎使用它。

除了SimpleDateFormat,其實很多時候,面對全局變量,我們都需要考慮并發情況是否存在問題,如下

 

  1.   @Component 
  2.   public class Test { 
  3.   
  4.      public static List<String> desc = new ArrayList<>(); 
  5.    
  6.       public List<String> getDescByUserType(int userType) { 
  7.           if (userType == 1) { 
  8.               desc.add("普通會員不可以發送和查看郵件,請購買會員"); 
  9.               return desc
  10.          } else if (userType == 2) { 
  11.              desc.add("恭喜你已經是VIP會員,盡情的發郵件吧"); 
  12.             return desc
  13.          }else { 
  14.              desc.add("你的身份未知"); 
  15.              return desc
  16.          } 
  17.      } 
  18.  } 

因為desc是全局變量,在并發情況下,請求getDescByUserType方法,得到的可能并不是你想要的結果。

Trible Kill

假設現在有如下業務:控制同一個用戶訪問某個接口的頻率不能小于5秒。一般很容易想到使用redis的 setnx操作來控制并發訪問,于是有以下代碼:

 

  1.  if(RedisOperation.setnx(userId, 1)){ 
  2.      RedisOperation.expire(userId,5,TimeUnit.SECONDS)); 
  3.       //執行正常業務邏輯 
  4.  }else
  5.       return “訪問過于頻繁”; 
  6.  } 

假設執行完setnx操作,還沒來得及設置expireTime,機器重啟或者突然崩潰,將會發生死鎖。該用戶id,后面執行setnx永遠將為false,這可能讓你永遠損失那個用戶。

那么怎么解決這個問題呢,可以考慮用SET key value NX EX max-lock-time ,它是一種在 Redis 中實現鎖的方法,是原子性操作,不會像以上代碼分兩步執行,先set再expire,它是一步到位。

客戶端執行以上的命令:

  • 如果服務器返回 OK ,那么這個客戶端獲得鎖。
  • 如果服務器返回 NIL ,那么客戶端獲取鎖失敗,可以在稍后再重試。
  • 設置的過期時間到達之后,鎖將自動釋放

Quadra Kill

我們看一下有關ConcurrentHashMap的一段代碼,如下:

 

  1.   //全局變量 
  2.   Map<String, Integer> map = new ConcurrentHashMap();  
  3.   
  4.  Integer value = count.get(k); 
  5.  if(value == null){ 
  6.          map.put(k,1); 
  7.   }else
  8.       map.put(k,value+1); 
  9.   } 

假設兩條線程都進入 value==null,這一步,得出的結果是不是會變小?OK,客官先稍作休息,閉目養神一會,我們驗證一下,請看一個demo:

 

  1.   public static void main(String[] args)  { 
  2.           for (int i = 0; i < 1000; i++) { 
  3.               testConcurrentMap(); 
  4.           } 
  5.       } 
  6.       private static void testConcurrentMap() { 
  7.           final Map<String, Integercount = new ConcurrentHashMap<>(); 
  8.           ExecutorService executorService = Executors.newFixedThreadPool(2); 
  9.          final CountDownLatch endLatch = new CountDownLatch(2); 
  10.          Runnable task = ()->  { 
  11.                  for (int i = 0; i < 5; i++) { 
  12.                      Integer value = count.get("k"); 
  13.                      if (null == value) { 
  14.                          System.out.println(Thread.currentThread().getName()); 
  15.                          count.put("k", 1); 
  16.                      } else { 
  17.                          count.put("k", value + 1); 
  18.                      } 
  19.                  } 
  20.                  endLatch.countDown(); 
  21.          }; 
  22.   
  23.          executorService.execute(task); 
  24.          executorService.execute(task); 
  25.  
  26.         try { 
  27.              endLatch.await(); 
  28.              if (count.get("k") < 10) { 
  29.                  System.out.println(count); 
  30.              } 
  31.         } catch (Exception e) { 
  32.             e.printStackTrace(); 
  33.         } 

表面看,運行結果應該都是10對吧,好的,我們再看運行結果:

 

 

 

運行結果出現了5,所以這樣實現是有并發問題的,那么正確的實現姿勢是啥呢?

 

  1.   Map<K,V> map = new ConcurrentHashMap();  
  2.   V v = map.get(k); 
  3.   if(v == null){ 
  4.           V v = new V(); 
  5.           V old = map. putIfAbsent(k,v); 
  6.           if(old != null){ 
  7.                    v = old; 
  8.           } 
  9.   } 

可以考慮使用putIfAbsent解決這個問題

(1)如果key是新的記錄,那么會向map中添加該鍵值對,并返回null。

(2)如果key已經存在,那么不會覆蓋已有的值,返回已經存在的值

我們再來看看以下代碼以及運行結果:

 

  1.   public static void main(String[] args)  { 
  2.           for (int i = 0; i < 1000; i++) { 
  3.               testConcurrentMap(); 
  4.           } 
  5.       } 
  6.    
  7.       private static void testConcurrentMap() { 
  8.           ExecutorService executorService = Executors.newFixedThreadPool(2); 
  9.           final Map<String, AtomicInteger> map = Maps.newConcurrentMap(); 
  10.          final CountDownLatch countDownLatch = new CountDownLatch(2); 
  11.   
  12.          Runnable task = ()->  { 
  13.                  AtomicInteger oldValue; 
  14.                  for (int i = 0; i < 5; i++) { 
  15.                      oldValue = map.get("k"); 
  16.                     if (null == oldValue) { 
  17.                          AtomicInteger initValue = new AtomicInteger(0); 
  18.                          oldValue = map.putIfAbsent("k", initValue); 
  19.                          if (oldValue == null) { 
  20.                              oldValue = initValue; 
  21.                          } 
  22.                      } 
  23.                      oldValue.incrementAndGet(); 
  24.                  } 
  25.              countDownLatch.countDown(); 
  26.          }; 
  27.   
  28.          executorService.execute(task); 
  29.          executorService.execute(task); 
  30.   
  31.          try { 
  32.              countDownLatch.await(); 
  33.              System.out.println(map); 
  34.          } catch (Exception e) { 
  35.              e.printStackTrace(); 
  36.       } 
  37.     }

 

 

 

Penta Kill

現有如下業務場景:用戶手上有一張現金券,可以兌換相應的現金,

錯誤示范一

 

  1. if(isAvailable(ticketId){ 
  2.     1、給現金增加操作 
  3.     2、deleteTicketById(ticketId) 
  4. }else
  5.     return “沒有可用現金券” 

解析: 假設有兩條線程A,B兌換現金,執行順序如下:

 

 

 

 

1.線程A加現金

2.線程B加現金

3.線程A刪除票標志

4.線程B刪除票標志

顯然,這樣有問題了,已經給用戶加了兩次現金了。

錯誤示范2

 

  1.   if(isAvailable(ticketId){ 
  2.       1、deleteTicketById(ticketId) 
  3.       2、給現金增加操作 
  4.   }else
  5.      return “沒有可用現金券” 
  6.   } 

并發情況下,如果一條線程,第一步deleteTicketById刪除失敗了,也會多添加現金。

正確處理方案

 

  1.   if(deleteAvailableTicketById(ticketId) == 1){ 
  2.       1、給現金增加操作 
  3.   }else
  4.       return “沒有可用現金券” 
  5.   } 

 

責任編輯:華軒 來源: 撿田螺的小男孩
相關推薦

2024-10-29 09:42:50

2021-06-15 06:04:42

MySQL數據庫索引

2017-10-18 13:28:27

語言Python開發錯誤

2022-07-14 07:12:09

PythonPandasVBA

2013-06-28 11:08:07

運維DBASA

2016-11-02 12:56:58

Linux新手錯誤

2022-12-26 11:02:06

云計算CIOIT

2019-11-01 14:19:02

大數據機器學習工具

2009-03-27 10:25:24

OracleDBA職責

2020-07-15 07:53:41

VSCode Task腳本命令

2022-06-28 10:13:09

Pandas錯誤Python

2025-06-10 03:00:00

2021-06-27 06:25:14

代碼優化技巧Java

2011-05-31 15:38:37

CSS

2022-05-17 09:32:24

Bash編程Linux

2015-08-26 10:00:31

獨立游戲cp錯誤

2023-06-13 11:11:14

2011-04-18 12:55:04

JavaScript開發者

2023-07-19 11:19:37

AI項目人工智能

2019-04-24 08:56:34

Java開發人員常犯錯誤
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产一区一区二区三亚瑟 | 欧美精品被 | 天堂亚洲 | 黄色大片免费看 | 亚洲男人天堂网 | 久久国产一区 | 国产精品精品视频一区二区三区 | 久久久久国产 | 成人一区av| 成人午夜性成交 | 欧美黄色一区 | 国产高清视频在线观看 | 成人深夜福利在线观看 | 亚洲视频在线免费 | av中文网 | 97国产在线视频 | 日韩欧美大片在线观看 | 高清亚洲 | 人人看人人爽 | 免费h视频 | 亚洲一区视频在线 | 欧美激情精品久久久久久 | 夜夜操天天艹 | 国产日批 | 国产乱码精品一区二三赶尸艳谈 | 曰韩三级 | 国产精品久久久久久久久免费桃花 | 国产一区二区在线播放 | 中文在线a在线 | 午夜资源 | 亚洲自拍偷拍免费视频 | 在线91| 欧美日韩亚洲国产 | 亚洲一区二区三区免费在线观看 | 日韩中文字幕在线观看 | 九九爱这里只有精品 | 精品一区二区三区在线观看国产 | 在线电影日韩 | 国精产品一区二区三区 | 色精品 | 亚洲另类视频 |