J.U.C--同步容器类Collections

本文介绍了Java中的Collections类如何将非线程安全的容器如ArrayList、HashMap等转换为线程安全的同步容器,重点分析了SynchronizedList的实现原理,展示了通过内部类和同步代码块实现线程安全的机制。同时,文章讨论了同步容器在迭代时的线程安全问题及可能的性能影响。
摘要由CSDN通过智能技术生成

在JDK1.2之前同步容器类包括Vector、HashTable,这两个容器通过内置锁synchronized保证了同步。后面的ArrayList、LinkedList、HashMap、LinkedHashMap等等都不是线程安全的,没有添加同步机制。但是JDK后面的Collections类也提供了这些常见容器类的同步容器类实现:将他们的状态封装起来,并对每个公共方法都进行同步,使得每次只有一个线程能访问容器状态。

Collections类

在Collections这个类下面提供了一些静态类和静态方法。比如常见的同步容器类的创建方法:

public static <T> List<T> synchronizedList(List<T> list);

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m);

public static <T> Set<T> synchronizedSet(Set<T> s);

public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m);

.....

通过这些静态方法将一个传递进去的集合包装成同步的集合。这里很明显就用到了设计模式中的装饰器模式。

装饰器接受一个接口对象,并返回一个同样接口的对象,不过,新对象可能会扩展一些新的方法或属性,扩展的方法或属性就是所谓的”装饰”,也可能会对原有的接口方法做一些修改,达到一定的”装饰”目的。

Collections有三组装饰器方法,它们的返回对象都没有新的方法或属性,但改变了原有接口方法的性质,经过”装饰”后,它们更为安全了,具体分别是写安全、类型安全和线程安全,这里我们只介绍线程安全的装饰类。

由于篇幅有限,我这里只举一个例子,就是:synchronizedList(List<T> list); 包装的List的线程安全的实现。
老规矩,还是来看源码:
Collections.synchronizedList(new ArrayList<String>());
看看这个static方法的调用发生了什么?

//synchronizedList函数源码
public static <T> List<T> synchronizedList(List<T> list) {
    return (list instanceof RandomAccess ?
            new SynchronizedRandomAccessList<>(list) :
            new SynchronizedList<>(list));
}

我们发现这个函数源码里面实际上是new了一个SynchronizedList<>(list))对象,该对象是collections的内置静态类。并传入参数list。 继续向下看看这个静态类的构造器:

SynchronizedList(List<E> list) {
   super(list);
   this.list = list;
}

其实就是一个传参的过程。下面看看SynchronizedList这个类的属性:

final List<E> list;

一个final修饰的list,表示list一旦传递了引用就不可变。也就是说原本的参数list引用传递给SynchronizedList的list并不可变。

然后我们看一下get()函数的源码:

public E get(int index) {
       synchronized (mutex) {
    }
}

这个时候就看到了同步的实现了,通过synchronized加锁实现同步代码块,使得每次只有一个线程能访问容器状态。这里的锁对象mutex是来源于父类SynchronizedCollection。看下源码就知道SynchronizedList的所有公共方法都是通过给mutex加锁实现的容器状态访问的同步操作。

其余的同步容器类的实现都是类似的,就不一一分析了。

同步容器类的迭代的线程安全

如果同步容器类的数据量很大,迭代的时候占用时间较长。迭代过程中可能会出现别的线程修改了容器的数据,这样迭代的时候可能会抛出异常(比如迭代到索引是最后一个元素的元素,这时候另外一个线程删除了这个线程,这时候会抛出数组越界的异常)。

但是如果迭代过程中加锁,那么就会出现性能问题。其中一种解决方案就是克隆容器,再迭代克隆的容器。(克隆期间需要加锁),但是克隆也是耗费CPU性能的。所以没有十全十美的办法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值