java并发编程实践中摘录的重点-绿色文字为jdk文档中复制的
同步容器
    
    同步容器类包括两部分
            1.vector 和 hashtable (早起jdk的一部分)
            2.Collections.synchronizedXXX工厂方法创建的(jdk1.2中才加入的)
    同步容器都是线程安全的,但是对于复合操作,需要额外的客户端加锁(jdk文档中有明确的说明)。
        复合操作:
                迭代iterate
                导航navigation
                条件运算 (比如:list操作,缺少即加入--先检查list中是否存在,不存在则加入)
    以下内容复制自jdk6.0文档

synchronizedList

public static <T> List<T> synchronizedList(List<T> list)
返回指定列表支持的同步(线程安全的)列表。为了保证按顺序访问,必须通过返回的列表完成所有对底层实现列表的访问。

在返回的列表上进行迭代时,用户必须手工在返回的列表上进行同步:

  List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }
 
不遵从此建议将导致无法确定的行为。
 

由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败(fail-fast ):如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。

 
通过在迭代期间持有同步容器的锁,防止其他线程在迭代期间修改该容器(例如:迭代时其他线程remove元素,造成迭代时抛出ConcurrentModificationException,使用for each迭代时也会出现这种情况)
 
隐性迭代
    toString,hashCode,equalse,containsAll,removeAll,retainAll等方法都会隐式的Iterate,可能抛出ConcurrentModificationException
 
同步容器通过对容器所有状态(容器中的所有元素)进行串行访问,从而实现线程安全,代价是削弱了并发性
同步容器仅仅是全部的方法使用synchronized修饰了,一个公共锁同步每一个方法,并严格的限制了只能一个线程同时访问容器。
 
并发容器(jdk5.0新增的)
 
     concurrentHashMap 
 
        使用更加细化的锁机制 -- 分离锁(允许更深层次的共享访问,为并发带来更高的吞吐量)
            任意数量的读线程可以并发的访问map;读,写线程可以并发访问;允许并发修改map
 
        提供的迭代器具有 弱一致性(非 快速失败的)迭代器允许并发修改。迭代器被设计成每次仅由一个线程使用( 单一线程迭代)。
 
        提供了常见复合操作,以原子方式执行的            

            putIfAbsent

            public V putIfAbsent(K key,
                     V value)
如果指定键已经不再与某个值相关联,则将它与给定值关联。这等价于:
   if (!map.containsKey(key)) 
      return map.put(key, value);
  else
       return map.get(key);
            replace
        public boolean replace(K key,
                       V oldValue,
                       V newValue)
            只有目前将键的条目映射到给定值时,才替换该键的条目。这等效于:
   if (map.containsKey(key) && map.get(key).equals(oldValue)) {
       map.put(key, newValue);
       return true;
   } else return false;  

            remove

            public boolean remove(Object key,
                              Object value)
            只有目前将键的条目映射到给定值时,才移除该键的条目。这等效于:
   if (map.containsKey(key) && map.get(key).equals(value)) {
       map.remove(key);
       return true;
   } else return false;
其他方法请参阅jdk文档
    
CopyOnWriteArrayList
         
        同步list的一个并发代替品
        写入时复制--
             理论依据 只要有效的不可变对象被正确的发布,访问它将不再需要更多的同步 ???
             修改时 创建并重新发布一个新的容器拷贝,以此来实现可变性???
            允许多线程迭代访问,迭代器返回的元素严格与迭代器创建时一致,不会考虑后续的修改~
 
         对容器的迭代操作远远高于对容器的修改操作频率时使用 -- 修改时拷贝需要一定的开销
        
        与之类似的还有 CopyOnWriteArraySet(同步set的并发替代品) 
 
大多数情况下可以用 并发容器取代 同步容器
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐