JUC -集合不安全:

集合不安全:

  1. 常用的集合List:ArrayList线程不安全 多线程环境中会出现并发修改异常 下面的代码中使用 30个线程进行修改

    • 具体问题:

          public static void main(String[] args) {
      
              List<String> list = new ArrayList<>();
              for (int i = 0; i < 3; i++) {
                  new Thread(() ->{
                      String replace = UUID.randomUUID().toString().substring(0, 5);
                      list.add(replace);
                  }).start();
              }
              System.out.println(list);
          }
      

      在这里插入图片描述

    • 解决方式:

      • 最粗暴的方式加锁: 使用synchronized 也就是使用 Vector 能解决上述的问题:但是出现了一个新的问题(数据的一致性得以实现 但是并发性降低 每次只能有一个线程执行写操作 非常的慢)
        在这里插入图片描述
      • Java api 中获取解决方式 将线程不安全的ArrayList转变成为线程安全的List 但是也不是能完全的实现线程安全 比如进行迭代操作:还是会出现上述的异常
         List<String> list = Collections.synchronizedList(new ArrayList<>());
        
        • 查看源码可知:大部分的操作都是使用synchronized 但是 iterator方法确实需要使用者手动的进行同步 非常的坑!!
          在这里插入图片描述
    • CopyOnWriteArrayList 从字面可以知道,CopyOnWriteArrayList在线程对其进行些操作的时候,会拷贝一个新的数组以存放新的字段(读写分离)操作的代码如下:底层使用的是volatile 而不是比较重的synchronized 比较推荐的方式

      /** The lock protecting all mutators */
          transient final ReentrantLock lock = new ReentrantLock();
       
          /** The array, accessed only via getArray/setArray. */
          private volatile transient Object[] array;//保证了线程的可见性
      	
      	 public boolean add(E e) {
      	final ReentrantLock lock = this.lock;//ReentrantLock 保证了线程的可见性和顺序性,即保证了多线程安全。
      	lock.lock();
      	try {
      	    Object[] elements = getArray();
      	    int len = elements.length;
      	    Object[] newElements = Arrays.copyOf(elements, len + 1);//在原先数组基础之上新建长度+1的数组,并将原先数组当中的内容拷贝到新数组当中。
      	    newElements[len] = e;//设值
      	    setArray(newElements);//对新数组进行赋值
      	    return true;
      	} finally {
      	    lock.unlock();
      	}
        }
      
      • 其没有加任何同步关键字,根据以上写操作的代码可知,其每次写操作都会进行一次数组复制操作,然后对新复制的数组进行些操作,不可能存在在同时又读写操作在同一个数组上(不是同一个对象),而读操作并没有对数组修改,不会产生线程安全问题。Java中两个不同的引用指向同一个对象,当第一个引用指向另外一个对象时,第二个引用还将保持原来的对象。
      • 其中setArray()操作仅仅是对array进行引用赋值。Java中“=”操作只是将引用和某个对象关联,假如同时有一个线程将引用指向另外一个对象,一个线程获取这个引用指向的对象,那么他们之间不会发生ConcurrentModificationException,他们是在虚拟机层面阻塞的,而且速度非常快,是一个原子操作,几乎不需要CPU时间
      • 在列表有更新时直接将原有的列表复制一份,并再新的列表上进行更新操作,完成后再将引用移到新的列表上。旧列表如果仍在使用中(比如遍历)则继续有效。如此一来就不会出现修改了正在使用的对象的情况(读和写分别发生在两个对象上),同时读操作也不必等待写操作的完成,免去了锁的使用加快了读取速度
    • Collections.synchronizedList & CopyOnWriteArrayList适用的场景:

      • CopyOnWriteArrayList,发生修改时候做copy,新老版本分离,保证读的高性能,适用于以读为主,读操作远远大于写操作的场景中使用,比如缓存
      • Collections.synchronizedList则可以用在CopyOnWriteArrayList不适用,但是有需要同步列表的地方,读写操作都比较均匀的地方
  2. 常用集合Se:解决方式 和 List相同

    • Set 解决方式:
      • Set不安全解决方案一:collections().synchronizedSet(new Hashset());
        在这里插入图片描述
      • Set不安全解决方案二:copyOnWriteArraySet();
  3. Map解决方式:

    • 解决方式1:使用collections().synchronizedMap(new HashMap());

      public static void collections(){
      
             Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
      
             for (int i = 1; i <= 100; i++) {
                 new Thread(()->{
                     map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                     System.out.println(map);
                 },String.valueOf(i)).start();
             }
         }
      
    • 解决方式2 :ConcurrentHashMap

      在这里插入图片描述

         
      public static void concurrentHashMap(){
          /**
           * 针对Map,没有 CopyOnWrite**类,与之对应的是ConcurrentHashMap
           */
          Map<String, String> map = new ConcurrentHashMap<>();
      
          for (int i = 1; i <= 100; i++) {
              new Thread(()->{
                  map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                  System.out.println(map);
              },String.valueOf(i)).start();
          }
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上山打卤面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值