首页 > 其他分享 >ConcurrentHashMap并不是绝对线程安全的

ConcurrentHashMap并不是绝对线程安全的

时间:2023-06-27 15:32:12浏览次数:41  
标签:ConcurrentHashMap s2 绝对 widgetCacheMap 1L 线程 new public


ConcurrentHashMap是线程安全的概念已经深入人心,让我们在使用的时候有些大意了,我也懒得动脑子,直接使用,结果碰到钉子了. 
这个问题让我很郁闷,程序逻辑全是对的,但是问题却明明摆在那边,最后怀疑是HashMap的问题。 




    1. package
    2.   
    3. import
    4. import
    5. import
    6.   
    7. import
    8.   
    9. public class
    10.   
    11. private static Map<Long, ServiceDO> widgetCacheMap = new
    12. /**
    13.      * @param args
    14.      */
    15. public static void
    16. // TODO Auto-generated method stub
    17. for(int i=0;i<10000;i++){  
    18. new Thread(new
    19.             tt.start();  
    20.         }  
    21.     }  
    22.   
    23. static class Rund implements
    24.   
    25. public void
    26. // TODO Auto-generated method stub
    27.             test();  
    28.         }  
    29.   
    30. /**
    31.          * 1W次,总有那么几次线程不安全
    32.          */
    33. public void
    34. new
    35.                 tt.set();  
    36. int
    37.                 tt.change();  
    38. int
    39. if(s1==s2){  
    40. ":"+s2);  
    41.                 }  
    42.         }  
    43.   
    44.     }  
    45.   
    46.   
    47.   
    48. public void
    49. new
    50. new
    51. 1);  
    52.             mm.put(1L, ss);  
    53.             widgetCacheMap = mm;  
    54.     }  
    55. public void
    56. new
    57. new
    58. 2);  
    59.             mm.put(1L, ss);  
    60.             widgetCacheMap = mm;  
    61.     }  
    62.   
    63. }



    执行10000次,多执行几次,或许你会发现,真的一般情况下是线程安全的,但是在大量并发的时候,线程就变得不那么安全了. 
    输出结果如下: 



    1. 2:2
    2. 2:2
    3. 2:2


    为什么出现这种情况,我在第一个地方设置值,然后取值,第二个地方再设置值,然后取值,两个值应该不同的,判断相同的时候,既然出现了。有人怀疑是ConcurrentHashMap,那你可以换成HashMap试试.结果一样. 
    为什么是2,2不是1,1;当然一般情况下是1:2,并发情况下就变成2,2了. 
    有人怀疑是初始化widgetCacheMap的问题,那么改代码如下: 


    1. public void
    2. //Map mm= new HashMap();
    3. new
    4. 1);  
    5.         widgetCacheMap.put(1L, ss);  
    6. //widgetCacheMap = mm;
    7. }  
    8. public void
    9. //Map mm= new HashMap();
    10. new
    11. 2);  
    12.         widgetCacheMap.put(1L, ss);  
    13. //widgetCacheMap = mm;
    14. }


    真是不改不知道,一改吓一跳,这回出现刚才说的情况1,1 



    1. 1:1
    2. 2:2
    3. 2:2
    4. 2:2
    5. 2:2


    而且改了之后其并发问题更严重了,因为这里每一次put都需要加行锁,其并发的概念也就上升了. 
    推荐写法还是按第一次方法,对象的覆盖是原子的,最好加一把锁,否则你第一次覆盖了,第二次又被别人覆盖了. 
    于是代码如下: 



    1. public void
    2. synchronized
    3.   
    4. new
    5. new
    6. 1);  
    7.         mm.put(1L, ss);  
    8.         widgetCacheMap = mm;  
    9.   
    10.     }  
    11. }  
    12. public void
    13. synchronized
    14. new
    15. new
    16. 2);  
    17.         mm.put(1L, ss);  
    18.         widgetCacheMap = mm;  
    19.     }  
    20.   
    21. }


    保持widgetCacheMap的变更成原子状态。当然还会出现上面的情况,这是为什么呢。 
    因为每一个线程获取的时候,可能取的是原子1,也可能是原子2,如果在多线程获取的时候加一把锁,那么获取的就是原子X,但至少是一个原子,要么1,要么2. 
    于是代码如下: 




      1. public void
      2. synchronized
      3. new
      4.             tt.set();  
      5. int
      6.             tt.change();  
      7. int
      8. if(s1==s2){  
      9. ":"+s2);  
      10.             }  
      11.         }  
      12.     }



      结果又出现如上现象,这是为什么呢,因为锁里面还加着锁,锁最好是原子化,尽量保持最小范围,不能价懒,像我一样就悲剧了. 




        1. /**
        2.  * 1W次,总有那么几次线程不安全
        3.  */
        4. public void
        5. new
        6.         tt.set();  
        7. int s1 = -1;  
        8. synchronized
        9.           s1 = widgetCacheMap.get(1L).getStatus();  
        10.         }  
        11.         tt.change();  
        12. int s2 = -2;  
        13. synchronized
        14.           s2 = widgetCacheMap.get(1L).getStatus();  
        15.         }  
        16. if(s1==s2){  
        17. ":"+s2);  
        18.         }  
        19. }



        还是出现上面这种情况,通阅全码,发现每一次都是原子了,应该没问题了。 
        但是还需要考虑run方法是多线程的,只有一个线程进入test,那就算原子了.如下: 
        唉,这是为什么呢,syn不起作用? 
        开始怀疑,于是去掉所有的syn,只添加run方法中的如下: 



        1. /**
        2.      * 1W次,总有那么几次线程不安全
        3.      */
        4. public   void
        5. synchronized
        6. new
        7.             tt.set();  
        8. int s1 = -1;  
        9.   
        10.               s1 = widgetCacheMap.get(1L).getStatus();  
        11.             tt.change();  
        12. int s2 = -2;  
        13.               s2 = widgetCacheMap.get(1L).getStatus();  
        14.   
        15. if(s1==s2){  
        16. ":"+s2);  
        17.             }  
        18.         }  
        19.     }

        整个进行原子操作,结果让人晕死。还是出现在,最后想了想,原来Hash或者CurrentHashMap也一样,在中间change了一下,而syn锁定的是一个不变的东西。 
        于如改代码如下: 



        1. /**
        2.      * 1W次,总有那么几次线程不安全
        3.      */
        4. public   void
        5. synchronized ("") {  
        6. new
        7.             tt.set();  
        8. int s1 = -1;  
        9.   
        10.               s1 = widgetCacheMap.get(1L).getStatus();  
        11.             tt.change();  
        12. int s2 = -2;  
        13.               s2 = widgetCacheMap.get(1L).getStatus();  
        14.   
        15. if(s1==s2){  
        16. ":"+s2);  
        17.             }  
        18.         }  
        19.     }


        这回你怎么执行都是原子操作了。 

        总结:ConcurrentHashMap是线程安全的,那是在他们的内部操作,其外部操作还是需要自己来保证其同步的,特别是静态的ConcurrentHashMap,其有更新和查询的过程,要保证其线程安全,需要syn一个不可变的参数才能保证其原子性

        标签:ConcurrentHashMap,s2,绝对,widgetCacheMap,1L,线程,new,public
        From: https://blog.51cto.com/u_16161240/6563501

        相关文章

        • java线程监控-jstack+jvisualvm
          Java线程监控一.Jstasck1.查找进程ps-ef|greptomcat-oa2.使用jstack监控jstack2429二、jvisualvm1.tomcat应用环境配置1.1tomcat环境配置修改tomcat中,catalina.sh文件cd/usr/src/tomcat-pinter/binvicatalina.sh在第二行添加如下:JAVA_OPTS="-Dcom.sun.mana......
        • 线程池处理异步任务
          点击查看代码/***异步任务线程池(单例)*用于异步执行任务*/publicclassThreadPoolSingleton{privatestaticfinalAtomicReference<ThreadPoolSingleton>INSTANCE=newAtomicReference<>();privatefinalExecutorServiceexecutor;privateTh......
        • tqt507 pthread 多线程测试 atomic 原子操作
          ////tqt057测试结果find/opt-name*atomic.h*//aarch64-linux-gnu-gccdemoatomic.c-odemoatomic-pthread-I/opt/EmbedSky/TQT507/CoreA/longan/kernel/linux-4.9/tools/include//cp./demoatomic/home/book/nfs_rootfs#include<linux/atomic.h>#inclu......
        • Java程序CPU消耗分析之找出最耗CPU线程
          java程序CPU消耗过高一般有两种情况:1、us过高,应用占用CPU资源过高,需找出具体占用CPU的线程所执行的代码,分析定位问题原因。分析步骤如下:(1)使用top命令找出占用cpu最高的JAVA进程(2)找出占用cpu最高的线程top-Hp1781(3)占CPU最高线程17596换算成16......
        • jmeter中跨线程调用变量
          Jmeter中跨线程调用变量一.Jmeter中线程运行规则1.各个线程组是完全独立的,每个线程组是不同的业务,互不影响2.线程组中的每个线程也是完全独立3.线程组中的每个线程,都是从上往下执行,完成一轮循环后,继续下一轮循环4.存在业务流或者接口之前存在依赖关系的放同一个线程组5.setup......
        • 多进程和多线程以及协程的创建模板
          【一】开启多进程的创建模板(基于Process模块)【1】方式一:创建多进程importmultiprocessingdefworker(n):"""子进程要执行的任务"""print(f'子进程{n}正在执行')defmain():task_lists=[]foriinrange(100)#创建一个进程对象......
        • spring中的bean是否是线程安全的
          Spring中的bean是否线程安全,与Spring本身是无关的。Spring中会提供很多线程安全方面的策略,因此Spring中的bean也不具备线程安全的特性在Spring的作用域中,有以下几种;prototype(多例)每次getBean得到时候都会创建一个新的对象singleton(单例)在Spring容器中只存在一个全局共......
        • 线程上运行 task
          我没能实现始终在一个线程上运行task 前文我们总结了在使用常驻任务实现常驻线程时,应该注意的事项。但是我们最终没有提到如何在处理对于带有异步代码的办法。本篇将接受笔者对于该内容的总结。如何识别当前代码跑在什么线程上一切开始之前,我们先来使用一种简单的方式来识......
        • 【3.0】知识点小结(线程相关)
          【3.0】知识点小结(线程相关)【一】什么是线程进程资源单位线程执行单位将操作系统比喻成大的工厂进程相当于工厂里面的车间线程相当于车间里面的流水线每一个进程必定自带一个线程进程:资源单位​ 起一个进程仅仅只是在内存空间中开辟出一块独立的空间......
        • 【4.0】知识点小结(线程进阶)
          【4.0】知识点小结(线程进阶)【一】什么是死锁与递归锁死锁是指两个或多个进程,在执行过程中,因争夺资源而造成了互相等待的一种现象。即两个或多个进程持有各自的锁并试图获取对方持有的锁,从而导致被阻塞,不能向前执行,最终形成僵局。在这种情况下,系统资源利用率极低,系统处于一种......