【集合类型的并发】Collections.synchronizedList

摘要: 详细的解析:Collections.synchronizedList
    1 :关注要点,为什么在有synchroniezed方法的同时会出现 Collections.synchronizedList
    2 :知识背景: 您可能需要了解java Synchronized方法的加锁的各种机制,包括如何上锁,锁对象
    3 : plus: 您需要不断的深化 Java加锁的各种机制

@NotThreadSafe
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}  

  这个示例希望实现的功能是为List提供一个原子操作:若没有则添加。因为ArrayList本身不是线程安全的,所以通过集合Collections.synchronizedList将其转换为一个线程安全的类,然后通过一个辅助的方法来为List实现这么个功能。初看起来这个方法没问题,因为也添加了synchronized关键字实现加锁了。

 但是仔细分析,你会发现问题。首先对于synchronized关键字,需要说明的是,它是基于当前的对象来加锁的,上面的方法也可以这样写:

public boolean putIfAbsent(E x) {
    synchronized(this) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}  

  所以这里的锁其实是BadListHelper对象, 而可以肯定的是Collections.synchronizedList返回的线程安全的List内部使用的锁绝对不是BadListHelper的对象,应为你在声明和初始化这个集合的过程之中,你尚且都不知道这个对象的存在。所以BadListHelper中的putIfAbsent方法和线程安全的List使用的不是同一个锁,因此上面的这个加了synchronized关键字的方法依然不能实现线程安全性。

 下面给出书中的另一种正确的实现:

@ThreadSafe
class GoodListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
                list.add(x);
            return absent;
        }
    }
}
  如果你要分析这个实现是否正确,你需要搞清楚Collections.synchronizedList返回的线程安全的List内部使用的锁是哪个对象,所以你得看看Collections.synchronizedList这个方法的源码了。该方法源码如下:
public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<T>(list) :
                new SynchronizedList<T>(list));
    }  

 通过源码,我们还需要知道ArrayList是否实现了RandomAccess接口:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable  

  查看ArrayList的源码,可以看到它实现了RandomAccess,所以上面的synchronizedList放回的应该是SynchronizedRandomAccessList的实例。接下来看看SynchronizedRandomAccessList这个类的实现:

static class SynchronizedRandomAccessList<E>  extends SynchronizedList<E>  implements RandomAccess {
        SynchronizedRandomAccessList(List<E> list) {
            super(list);
        }
    SynchronizedRandomAccessList(List<E> list, Object mutex) {
            super(list, mutex);
        }
    public List<E> subList(int fromIndex, int toIndex) {
        synchronized(mutex) {
                return new SynchronizedRandomAccessList<E>(
                    list.subList(fromIndex, toIndex), mutex);
            }
        }
        static final long serialVersionUID = 1530674583602358482L;
        private Object writeReplace() {
            return new SynchronizedList<E>(list);
        }
    }
 因为SynchronizedRandomAccessList这个类继承自SynchronizedList,而大部分方法都在SynchronizedList中实现了,所以源码中只包含了很少的方法,但是通过subList方法,我们可以看到这里使用的锁对象为mutex对象,而mutex是在SynchronizedCollection类中定义的,所以再看看SynchronizedCollection这个类中关于mutex的定义部分源码:
Java代码  收藏代码

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 3053995032091335093L;
    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize  

    SynchronizedCollection(Collection<E> c) {
            if (c==null)
                throw new NullPointerException();
        this.c = c;
            mutex = this;
        }  

    SynchronizedCollection(Collection<E> c, Object mutex) {
        this.c = c;
            this.mutex = mutex;
        }
}
  可以看到mutex就是当前的SynchronizedCollection对象,而SynchronizedRandomAccessList继承自SynchronizedList,SynchronizedList又继承自SynchronizedCollection,所以SynchronizedRandomAccessList中的mutex也就是SynchronizedRandomAccessList的this对象。所以在GoodListHelper中使用的锁list对象,和SynchronizedRandomAccessList内部的锁是一致的,所以它可以实现线程安全性。
class BadListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());  

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}  

putIfAbsent方法和List并不是使用的同一个锁对象,List使用的锁对象并不是BadListHelper,而是list。假如A线程进入putIfAbsent方法,list这个锁并没有被获取(A线程获取的是 BadListHelper这个对象),所以其他线程还能够获得list锁对象来改变list对象。boolean absent = !list.contains(x);当线程到这串代码结束时,其他线程获得list锁对象,从而就能调用list的方法来改变list对象,这时候就可能导致!list.contains(x)改变,即域absent并不是A线程得到的布尔类型。所以这个类并不是线程安全的。

获得变量的锁就可以改变变量,没有获得变量锁的就不能改变,获得方法的锁就可以执行方法里面的语句。

时间: 2024-11-05 14:25:26

【集合类型的并发】Collections.synchronizedList的相关文章

[Guava学习笔记]Collections: 不可变集合, 新集合类型

不可变集合 不接受null值. 创建:ImmutableSet.copyOf(set); ImmutableMap.of(“a”, 1, “b”, 2); public static final ImmutableSet<Color> GOOGLE_COLORS = ImmutableSet.<Color>builder() .addAll(WEBSAFE_COLORS) .add(new Color(0, 191, 255)) .build(); 可以有序(如ImmutableS

Java集合类型详解

这篇文章总结了所有的Java集合(Collection).主要介绍各个集合的特性和用途,以及在不同的集合类型之间转换的方式. Arrays Array是Java特有的数组.在你知道所要处理数据元素个数的情况下非常好用.java.util.Arrays 包含了许多处理数据的实用方法: Arrays.asList:可以从 Array 转换成 List.可以作为其他集合类型构造器的参数. Arrays.binarySearch:在一个已排序的或者其中一段中快速查找. Arrays.copyOf:如果你

ConcurrentHashMap和 CopyOnWriteArrayList提供线程安全性和可伸缩性 以及 同步的集合类 Hashtable 和 Vector Collections.synchronizedMap 和 Collections.synchronizedList 区别缺点

ConcurrentHashMap和 CopyOnWriteArrayList提供线程安全性和可伸缩性 DougLea的 util.concurrent 包除了包含许多其他有用的并发构造块之外,还包含了一些主要集合类型 List 和 Map 的高性能的.线程安全的实现.在本月的 Java理论与实践中,BrianGoetz向您展示了用 ConcurrentHashMap 替换 Hashtable 或 synchronizedMap ,将有多少并发程序获益. 在Java类库中出现的第一个关联的集合类

CopyOnWriteArrayList与Collections.synchronizedList的性能对比

列表实现有ArrayList.Vector.CopyOnWriteArrayList.Collections.synchronizedList(list)四种方式. 1 ArrayList ArrayList是非线性安全,此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificatio

C#集合类型

求职笔试遇到一道题: 列举C#中的集合类型___; 列举C#中的并发集合类型__; 我认识不深,查找的答案,希望大家指正: 集合类型是数据集合的常见变体,例如哈希表.队列.堆栈.字典和列表.集合基于ICollection 接口.IList 接口.IDictionary 接口,或其泛型集合中的相应接口. 1.数组集合类型:描述允许将数组视为集合处理的数组功能. 2.ArrayList 和List 集合类型:描述泛型和非泛型列表(最常用的集合类型)的功能. 3.Hashtable 和Dictiona

C#的集合类型及使用技巧

在日常开发过程中,我们不能避免的要对批量数据处理,这时候就要用到集合.集合总体上分为线性集合和非线性集合.线性集合是指元素具有唯一的前驱和后驱的数据结构类型:非线性集合是指有多个前驱和后驱的数据结构类型,如树和图.我们这里主要讲常用的线性集合,常用的线性集合有数组.ArrayList.List.Hashtable(哈希表).Dictionary(字典).Stack(堆栈集合).Queue(队列集合)等.一.数组数组是一个存储相同类型元素的固定大小的顺序集合.数组属于引用类型,它继承System.

Collections.synchronizedList 、CopyOnWriteArrayList、Vector介绍、源码浅析与性能对比【文末福利】

ArrayList线程安全问题 众所周知,ArrayList不是线程安全的,在并发场景使用ArrayList可能会导致add内容为null,迭代时并发修改list内容抛ConcurrentModificationException异常等问题.java类库里面提供了以下三个轮子可以实现线程安全的List,它们是 Vector Collections.synchronizedList CopyOnWriteArrayList 本文简要的分析了下它们线程安全的实现机制并对它们的读,写,迭代性能进行了对

集合类型

http://numbbbbb.gitbooks.io/-the-swift-programming-language-/content/chapter2/04_Collection_Types.html 本页包含内容: 数组(Arrays) 字典(Dictionaries) 集合的可变性(Mutability of Collections) Swift 语言提供经典的数组和字典两种集合类型来存储集合数据.数组用来按顺序存储相同类型的数据.字典虽然无序存储相同类型数据值但是需要由独有的标识符引用

C#集合类型——Array、ArrayList、List 之浅谈

在学习或工作中,集合是经常用到的,可以换一句话说“无项目无集合”,“项目皆有集合”.它一般存储一系列数据或者将一系列数据进行相关操作.在这里先大略谈一些集合类型的相关知识用于回顾. 数组(Array) 数组一般只存储一种类型的数据,有一维数组,二维数组等多维数组.都基于System.array类. 格式:type[]  typename:  type指类型,typename指数组名称. 一维数组有多种书写形式: int [] a={1,2,3}; int [] b=new int[]{1,2,3