多线程5 站在巨人的肩膀上
同步容器类
同步容器类是通过对自身状态的封装,并对每个public方法进行加锁同步。通过Collections.synchronizedXxx()等创建的容器就是同步容器类,常用的有:vector、hashtable。
那么同步同步容易类是线程安全的吗?会出现问题吗?
答案:是线程安全而且有可能出现很严重的问题!
容器上的常见符合操作若没有在客户端的代码上加锁来保护就有可能出现错误。例如:迭代、条件运算、跳转(根据指定顺序找到当前元素的下一个元素)
例举几种可能出现的情况和解决办法
1、同步容器类Vector上有两个方法getLast和deleteLast,都会有“如果没有就添加”的操作。如果线程A上调用getLast线程B上调用deleteLast,这些操作交替执行,如下图中请求一个不存在的元素,就会出错。这时候就要在客户端加锁,但会牺牲一些伸缩性和并发性。
A→ size(10) ——————→get(9)→错误
B→ size(10)→remove(9)
2、迭代的标准方式都是使用Iterrator。如果容器没有加锁,对容器进行迭代的时候,其他线程并发的修改了这个容器,就会出现一种及时失败的问题,抛出异常(ConcurrentModificationException)。他并不是一种完善的处理机制,所以只能用做预警指示器。想要避免就必须在迭代的过程持有容器的锁,如果是个共享容器就需要在所有迭代的地方加锁。也可以通过克隆容器并在副本上迭代解决(克隆的过程仍需加锁),但是有显著的性能开销取决因素可能有容器大小、元素执行的负责度、调用其他容器的频率、响应时间和吞吐量等
3、隐藏迭代 system.out.pringln(“字符串A”+容器A),编译器将字符转换为StringBuilder.append又会调用容器的toString,标准容器中的toString将进行迭代,这时候就有可能例2的情况。hashCode, equals, containsAll, removeAll, retainAll, 以其他容器为参数的构造器也会进行隐性迭代