Collections.synchronizedList

  • Collections.synchronizedList 包装 
    • 使用分析
      • 优点
        • 线程安全,synchronized 方法内代码级别加锁
      • 缺点
        • 性能问题:方法级别加synchronized锁
        • 复合操作非线程安全:但对于复合操作(如先检查列表是否包含某个元素,然后再添加该元素),Collections.synchronizedList 并不能保证其线程安全。需要开发者手动进行同步控制
        • 迭代器非线程安全:返回的列表的迭代器不是线程安全的。在使用迭代器遍历列表时,如果其他线程同时对列表进行修改,会抛出 ConcurrentModificationException 异常因此,在迭代过程中需要手动进行同步
      • 适用场景:
        • 对现有代码的改造
          • 如果项目中已经存在大量使用非线程安全 List 的代码,并且需要将其改为线程安全的,可以使用 Collections.synchronizedList 进行快速改造,而无需对代码结构进行大规模调整
        • 简单的线程安全需求
          • 当只需要对单个操作进行线程安全控制,且不涉及复合操作和复杂的并发场景时,
    • 设计分析
      • 设计目标
        • 将一个非线程安全的 List 转换为线程安全的 List
      • 设计思路
        • 解决问题的思路
          • 使用装饰设计模式,将被装饰的list的增删改查调用前增加synchronized代码块锁
      • 实现原理
        • 使用装饰设计模式,将被装饰的list的增删改查调用前增加synchronized代码块锁,锁对象是SynchronizedList实例
    • 源码分析
      • 继承关系
        • class SynchronizedList<E>
                  extends SynchronizedCollection<E>
                  implements List<E>
      • 构造方法
        • static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {

                  private static final long serialVersionUID = -7754090372962971524L;

                  //final属性的list
                  final List<E> list;

                  SynchronizedList(List<E> list) {
                      // 调用父类SynchronizedCollection的构造方法
                      super(list);
                      this.list = list;
                  }
                  SynchronizedList(List<E> list, Object mutex) {
                      super(list, mutex);
                      this.list = list;
                  }
      • synchronized锁对象mutex
        • 构造方法没有传入锁对象,则默认为mutex=this
        • 构造方法传入锁对象A,则为mutex=A
      • 操作方法使用synchronized代码块锁对象为mutex
        • // 注意:synchronizedList是用同步代码块给传入的集合对象加锁!
                  // 可以看到所有的操作都是上了锁的,synchronized (mutex),锁对象是mutex是来自SynchronizedCollection父类
                  public boolean equals(Object o) {
                      if (this == o) return true;
                      synchronized (mutex) {return list.equals(o);}
                  }
                  public int hashCode() {
                      synchronized (mutex) {return list.hashCode();}
                  }

                  public E get(int index) {
                      synchronized (mutex) {return list.get(index);}
                  }
                  public E set(int index, E element) {
                      synchronized (mutex) {return list.set(index, element);}
                  }

                  public void add(int index, E element) {
                      synchronized (mutex) { list.add(index, element); }
                  }

                  public E remove(int index) {
                      synchronized (mutex) { return list.remove(index); }
                  }

                  public int indexOf(Object o) {
                      synchronized (mutex) {return list.indexOf(o);}
                  }
                  public int lastIndexOf(Object o) {
                      synchronized (mutex) {return list.lastIndexOf(o);}
                  }

                  public boolean addAll(int index, Collection<? extends E> c) {
                      synchronized (mutex) {return list.addAll(index, c);}
                  }
      • 迭代器没有synchronized需要自己实现线程安全控制
        • // 但是有一个地方很值得注意的是迭代器的使用是没有上锁的,而且源码是标注了
                  // 需要用户去自己做同步处理, 迭代器,分割,流操作都需要自己实现同步处理。
                  public ListIterator<E> listIterator() {
                      return list.listIterator(); // Must be manually synched by user
                  }

                  public ListIterator<E> listIterator(int index) {
                      return list.listIterator(index); // Must be manually synched by user
                  }
        • 转换包装后的list可以实现add,remove,get等操作的线程安全性,但是对于迭代操作,Collections.synchronizedList并没有提供相关机制,所以迭代时需要对包装后的list(必须对包装后的list进行加锁,锁其他的不行)进行手动加锁。
          List list = Collections.synchronizedList(new ArrayList());
          //必须对list进行加锁
          synchronized (list) {
            Iterator i = list.iterator();
            while (i.hasNext())
                foo(i.next());
          }

          迭代操作必须加锁,可以使用synchronized关键字修饰;
          synchronized持有的监视器对象必须是synchronized (list),即包装后的list,使用其他对象如synchronized (new Object())会使add,remove等方法与迭代方法使用的锁不一致,无法实现完全的线程安全性。

      • https://blog.csdn.net/weixin_45480785/article/details/118934849
posted @ 2025-04-21 10:34  一点点征服  阅读(131)  评论(0)    收藏  举报