对JAVA线程安全的一点理解

      我们在很多地方都能够经常看到:HashMap线程不安全,HashTable线程安全;ArrayList线程不安全,Vector线程安全;StringBuilder线程不安全,StringBuffer线程安全。那究竟啥是线程安全,啥是线程不安全?

     简单点来说,线程安全,是指拥有共享数据的多个线程在并行执行的情况下,共享数据的改变在线程间能及时同步,线程内访问的都是最新的数据内容;

     那相对的,线程不安全,是指拥有共享数据的多个线程在并行执行的情况下,共享数据的改变在线程间不能及时同步,这就可能会导致线程内访问不到最新数据,从而引起数据被覆盖、丢失甚至引起线程等待过多,服务运行状态异常等问题。

     我们注意一下上面的定义,里面重点提到了多线程间的"共享数据",那什么样的数据才能称为"共享数据"?我理解的是,以线程的构造函数参数形式传入的数据。

以一个线程的构造函数为例:

Thread1(HashMap<String,String> map,CountDownLatch cdl){
this.map=map;
        this.cdl=cdl;
    }

    上面代码中第一个参数为HashMap,即为共享数据。每个线程都会在run方法中对该map进行操作。如果所有线程都仅仅是进行读操作,则此时HashMap不存在安全不安全的问题。只有当线程内对该map存在写操作时,才有可能会出现线程安全的问题。因为HashMap的读写操作都没有锁,所以无法保证读写的时序性。举个极端的情况,如果线程实例1向map中放入一个键值对,但尚未放入完成,此时线程实例2对map进行读,那线程2读到的数据必然不是最新的,那线程实例2中根据map中值进行的业务逻辑判断等,就会存在误差。

    上面的ArrayList、StringBuilder都是因为同样的原因,被称为线程不安全,即对数据的读写没有锁机制,无法保证数据的同步。锁机制的存在是保证了数据的同步,但是也因为锁的存在,代码的执行效率会受到较大影响,原先并行执行的内容,需要等待获取到锁才能进行下一步的执行。

    我们一定要区分开一种情况,如果是在线程内新定义的局部变量(不指向共享数据),是不存在什么线程安全不安全的问题的。所以,上面几种数据类型线程安全不安全,是根据使用场景来决定的,而不是类型本身。

    另外,除自己使用Synchronized关键字进行强制数据同步外,对HashMap、ArrayList也可以使用其他的改进机制:

      HashMap在多线程传参时,可以使用ConcurrentHashMap代替,比使用HashTable效率要高的多;

      ArrayList在多线程传参时,可以在定义时,使用Collections.synchronizedList()进行包裹,例如

        List<Map<String,Object>> list=Collections.synchronizedList(new ArrayList<Map<String,Object>>());//这种方式对效率影响较大,如果对效率要求不是很高,可以考虑采用。

     最后想说一下,如果大家在编码时,要使用多线程,请看一下多线程启动时的传参中是否存在线程不安全的数据类型,如果这个问题没注意,后面出现一些稀奇古怪的问题,不是很好定位。代码单步调试,也可能无法重现。

   

 

    

 

posted @ 2019-01-31 15:33  正直的不良帅  Views(282)  Comments(0)    收藏  举报