JUC-NotSafeDemo

请举例说明集合类是不安全的

一、如何证明线程是不安全的?

线程不安全错误:

java.util.ConcurrentModificationException 

ArrayList在迭代的时候如果同时对其进行修改就会 

抛出java.util.ConcurrentModificationException异常 

并发修改异常

原理:

 

List<String> list =  new  ArrayList<>(); 
for  ( int  i =  0 ; i < 30  ; i++) {
       
     
  new  Thread(()->{
                
    list .add(UUID. randomUUID ().toString().substring( 0 , 8 ));

        System. out .println( list );

   },String. valueOf (i)).start();
        } 
 看ArrayList的源码
public boolean  add( E  e) {

    ensureCapacityInternal( size  +  1 );
   // Increments modCount!!
   elementData [ size ++] = e;

   return true ;

} 没有
synchronized 线程不安全

二、解决方案

(1)vector

 

List<String> list =  new  Vector<>(); 
 
看Vector的源码 
public synchronized boolean  add( E  e) {
   
  modCount ++;
   
 ensureCapacityHelper( elementCount  +  1 );
    
 elementData [ elementCount ++] = e;
  
 return true ;

} 有
synchronized 线程安全  

 

(2)Collections

List<String> list = Collections. synchronizedList ( new  ArrayList<>()); 
Collections提供了方法 synchronizedList保证list是同步线程安全的 


那HashMap,HashSet是线程安全的吗?也不是 
所以有同样的线程安全方法 

 

(3)写时复制

 

List<String> list =  new  CopyOnWriteArrayList<>();

不加锁性能提升出错误,加锁数据一致性能下降

【1】CopyOnWriteArrayList的定义

A thread-safe variant of ArrayList in which all mutative operations (add, set, and so on) are implemented by making a fresh copy of the underlying array. 

CopyOnWriteArrayList是arraylist的一种线程安全变体, 

其中所有可变操作(add、set等)都是通过生成底层数组的新副本来实现的。

【2】举例

【3】CopyOnWrite理论

 

 
/**
 * Appends the specified element to the end of this list.
 *

 *  @param  e  element to be appended to this list
 
*  @return  { @code  true} (as specified by { @link  Collection#add})
 */public boolean  add( E  e) {
     
    final  ReentrantLock lock =  this . lock ;

    lock.lock();

     try  {

        Object[] elements = getArray();

         int  len = elements. length ;

        Object[] newElements = Arrays. copyOf (elements, len +  1 );

        newElements[len] = e;

        setArray(newElements);

         return true ;
    }  finally  {

            lock.unlock();
    
}
}     

CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,
而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。
添加元素后,再将原容器的引用指向新的容器setArray(newElements)
这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

【4】扩展类比

HashSet

Set<String> set =  new  HashSet<>();//线程不安全 
Set<String> set =  new  CopyOnWriteArraySet<>();//线程安全
 
HashSet底层数据结构是什么? 
HashMap  ? 
  
但HashSet的add是放一个值,而HashMap是放K、V键值对 

 
public  HashSet() {
    
map  =  new  HashMap<>();

}  
private static final  Object  PRESENT  =  new  Object();   public boolean  add( E  e) {
    

return  map .put(e,  PRESENT )== null ;

}
PRESENT 一个常量

 HashMap

Map<String,String> map =  new  HashMap<>();//线程不安全 

Map<String,String> map =  new  ConcurrentHashMap<>();//线程安全

例子

package  com.atguigu.gmall.jucdemo;



import  java.util.*;


import  java.util.concurrent.ConcurrentHashMap;
import  java.util.concurrent.CopyOnWriteArrayList;
import  java.util.concurrent.CopyOnWriteArraySet;



/**
 * 请举例说明集合类是不安全的
 */

public class  NotSafeDemo { 
     public static void  main(String[] args) { 

        Map<String,String> map =  new  ConcurrentHashMap<>(); 
         for  ( int  i =  0 ; i < 30  ; i++) {
             new  Thread(()->{ 
                 map .put(Thread. currentThread ().getName(),UUID. randomUUID ().toString().substring( 0 , 8 ));
                 System. out .println( map ); 
            },String. valueOf (i)).start();
         }


   
}

     
private static void  setNoSafe() {
         Set<String> set =  new  CopyOnWriteArraySet<>();
          for  ( int  i =  0 ; i < 30  ; i++) { 
             new  Thread(()->{ 
                 set .add(UUID. randomUUID ().toString().substring( 0 , 8 ));
                System. out .println( set );
             },String. valueOf (i)).start(); 
        }
   
}

     
private static void  listNoSafe() {
          //        List<String> list = Arrays.asList("a","b","c");
         //        list.forEach(System.out::println);
         //写时复制          List<String> list =  new  CopyOnWriteArrayList<>(); 
         // new CopyOnWriteArrayList<>(); 
        //Collections.synchronizedList(new ArrayList<>()); 
        //new Vector<>();//new ArrayList<>(); 

         for  ( int  i =  0 ; i < 30  ; i++) {
                      new  Thread(()->{
                          list .add(UUID. randomUUID ().toString().substring( 0 , 8 ));
                         System. out .println( list ); 
                    },String. valueOf (i)).start(); 
                }

    }


}





     
/**
     * 写时复制
     CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,
     而是先将当前容器Object[]进行Copy,复制出一个新的容器Object[] newElements,然后向新的容器Object[] newElements里添加元素。
     添加元素后,再将原容器的引用指向新的容器setArray(newElements)。
     这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
     所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

     *
     *
     *
      *

    public boolean add(E e) { 
        final ReentrantLock lock = this.lock; 
        lock.lock();
         try { 
            Object[] elements = getArray();
             int len = elements.length;
             Object[] newElements = Arrays.copyOf(elements, len + 1); 
            newElements[len] = e; 
            setArray(newElements);
             return true;
         } finally {
             lock.unlock(); 
        }
    }
     */

  

 

转载于:https://www.cnblogs.com/minmin123/p/11414176.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值