筆記:多線程訪問ConcurrentHashMap對key加鎖


轉載: https://www.cnblogs.com/qick/p/12494461.html

 

近期由於工作需要,要改寫以前的一個小項目,項目是C/S架構,server端部署在tomcat容器中。client端通過api請求訪問server端,因此會有並發的要求。

在server端,部署的時候我將初始化幾個對象,我將他們保存在map容器中,serverIp作為key,client端並發請求的時候則從這個map中以serverIp為key取出對應的對象來進行下一步操作。這樣一來,只要是請求的同一個serverIp,請求就會阻塞並先后執行,不同serverIp則會並發執行。

但是,放在map中的對象可能會由於某些原因失效,或不能用了,這樣的話就需要重新初始化這個對象,但是如果第一個請求進來時檢測對象需要初始化的時候同時有第二個請求進來的話,兩個請求都會去初始化這個對象,並會導致兩個請求拿到了不同的對象,會有問題。針對這個情況就需要加鎖,有兩個辦法:

一:可以在獲取map里面對應key的對象的方法上加鎖,但這樣的話就會阻塞掉不同key的並發請求。

二:對map中key對應的對象再次封裝,將每個serverIp作為key放入一個單獨的map中,再將所有map放入到一個全局唯一的map容器中,並發請求時,針對每一個獨立存放key的map加鎖。

為此,我寫了個demo來驗證第二個方案,特此記錄下來:

demo類:(用來模擬業務邏輯)

復制代碼
 1 public class daoDemo {
 2     private static Map<String,Map> map = new ConcurrentHashMap<>();
 3 
 4     public daoDemo(){
 5         getMap("one");
 6         getMap("two");
 7     }
 8 
 9     public static void getMap(String key){
10         Map<String,JSch> jSchMap = new ConcurrentHashMap<>();
11         jSchMap.put(key,new JSch());
12         map.put(key,jSchMap);
13     }
14 
15     public static void getInstance(jschBean t1, String key) throws InterruptedException{
16         Map<String,JSch> demo;
17         synchronized (demo = map.get(key)){
18             System.out.println(t1.getName()+" 拿到了demo對象: "+demo.hashCode());
19             Thread.sleep(500);
20             System.out.println(t1.getName()+"更新前:"+map.get(key).hashCode());
21             demo.put(key,new JSch());
22             map.put(key, demo);
23             System.out.println(t1.getName()+"更新后:"+map.get(key).hashCode());
24             System.out.println(t1.getName()+" 釋放了demo對象: "+demo.hashCode());
25         }
26 
27     }
28 }
復制代碼

線程實例類:(用來模擬並發請求)

復制代碼
 1 public class jschBean extends Thread{
 2 
 3     String key;
 4     int time;
 5 
 6     public jschBean(String name, String key, int time){
 7         this.setName(name);
 8         this.key = key;
 9         this.time = time;
10     }
11     @Override
12     public void run() {
13         for(int i=0;i<5;i++){
14             try{
15                 daoDemo.getInstance(this, this.key);
16                 Thread.sleep(time);
17             }catch (Exception e){
18                 e.printStackTrace();
19                 System.out.println(this.getName()+" : got exception...");
20             }
21         }
22     }
23 }
復制代碼

程序入口:

復制代碼
 1 public class syncDemo {
 2 
 3     public static void main(String[] args) {
 4         new daoDemo();
 5         jschBean jschBean1 = new jschBean("Thread-1","one",500);
 6         jschBean jschBean2 = new jschBean("Thread-2","one",1500);
 7         jschBean jschBean3 = new jschBean("Thread-3","two",500);
 8         jschBean jschBean4 = new jschBean("Thread-4","two",500);
 9         jschBean1.start();
10         jschBean3.start();
11         jschBean2.start();
12         jschBean4.start();
13     }
14 }
復制代碼

其中,jschBean1和jschBean2為一組,jschBean3和jschBean4為另一組,它們將會模擬兩個相同的請求去訪問同一個對象。 

 實驗結果:

 

 實驗結果:

Thread-1 拿到了demo對象: 17092794
Thread-3 拿到了demo對象: 924818233
Thread-1 更新前:17092794
Thread-3 更新前:924818233
Thread-1 更新后:490245531
Thread-3 更新后:1699709137
Thread-1 釋放了demo對象: 490245531
Thread-3 釋放了demo對象: 1699709137
Thread-2 拿到了demo對象: 490245531
Thread-4 拿到了demo對象: 1699709137
Thread-2 更新前:490245531
Thread-4 更新前:1699709137
Thread-2 更新后:775304961
Thread-2 釋放了demo對象: 775304961
Thread-1 拿到了demo對象: 775304961
Thread-4 更新后:2000928397
Thread-4 釋放了demo對象: 2000928397
Thread-3 拿到了demo對象: 2000928397
Thread-1 更新前:775304961
Thread-1 更新后:1119255817
Thread-1 釋放了demo對象: 1119255817
Thread-3 更新前:2000928397
Thread-3 更新后:672489720
Thread-3 釋放了demo對象: 672489720
Thread-4 拿到了demo對象: 672489720
Thread-4 更新前:672489720
Thread-1 拿到了demo對象: 1119255817
Thread-4 更新后:2038713877
Thread-4 釋放了demo對象: 2038713877
Thread-3 拿到了demo對象: 2038713877
Thread-1 更新前:1119255817
Thread-1 更新后:1950831806
Thread-1 釋放了demo對象: 1950831806
Thread-2 拿到了demo對象: 1950831806
Thread-3 更新前:2038713877
Thread-3 更新后:1994159918
Thread-3 釋放了demo對象: 1994159918
Thread-4 拿到了demo對象: 1994159918
Thread-2 更新前:1950831806
Thread-2 更新后:552717495
Thread-2 釋放了demo對象: 552717495
Thread-1 拿到了demo對象: 552717495
Thread-4 更新前:1994159918
Thread-4 更新后:2109462013
Thread-4 釋放了demo對象: 2109462013
Thread-3 拿到了demo對象: 2109462013
Thread-1 更新前:552717495
Thread-1 更新后:1685769729
Thread-1 釋放了demo對象: 1685769729
Thread-3 更新前:2109462013
Thread-3 更新后:78130730
Thread-3 釋放了demo對象: 78130730
Thread-4 拿到了demo對象: 78130730
Thread-1 拿到了demo對象: 1685769729
Thread-4 更新前:78130730
Thread-4 更新后:951665710
Thread-4 釋放了demo對象: 951665710
Thread-3 拿到了demo對象: 951665710
Thread-1 更新前:1685769729
Thread-1 更新后:814231480
Thread-1 釋放了demo對象: 814231480
Thread-2 拿到了demo對象: 814231480
Thread-3 更新前:951665710
Thread-3 更新后:325443208
Thread-3 釋放了demo對象: 325443208
Thread-4 拿到了demo對象: 325443208
Thread-2 更新前:814231480
Thread-2 更新后:617503857
Thread-2 釋放了demo對象: 617503857
Thread-4 更新前:325443208
Thread-4 更新后:1551467404
Thread-4 釋放了demo對象: 1551467404
Thread-2 拿到了demo對象: 617503857
Thread-2 更新前:617503857
Thread-2 更新后:1640810124
Thread-2 釋放了demo對象: 1640810124
Thread-2 拿到了demo對象: 1640810124
Thread-2 更新前:1640810124
Thread-2 更新后:1940031347
Thread-2 釋放了demo對象: 1940031347

 

從結果來看,線程1和線程2是相互阻塞的,並且他們拿到的總是map中保存的key對應對象的最新值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM