Java并发编程(4)-同步容器以及并发容器介绍

版权声明: https://blog.csdn.net/pbrlovejava/article/details/83351551


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


Java平台的类库包含了一个并发构造块的丰富集合,本篇将介绍在并发编程中十分常用的两种容器类,一是同步容器,二是并发容器,它们在某些特定的场合中可以用来替代我们常用的集合类,比如在高并发和多线程请求的情况下。
本篇总结自《Java并发编程实践》第五章 构造块 章节的内容,详情可以阅读该书。

一、同步容器

1.1、同步容器类介绍

同步容器类包括两部分,一个是Vector和Hashtable,它们是早期JDK的一部分;另一个是它们的同系容器,在JDK1.2之后才被 引入的同步包装类这些类是由Collections.synchronizedXxx工厂方法创建的。这些类通过封装它们的状态,并对每个公共方法进行了同步而实现了线程安全,这样一次只有一个线程能访问容器的状态。

1.2、常用的同步容器类

常用的同步容器类有Collections.synchronizedMap、Collections.synchronizedList、Collections.synchronizedSet、Collections.synchronizedSortSet、Collections.synchronizedSortMap,它们分别是Map、List、Set、SortSet、SortMap的同步容器类。

1.3、使用同步容器类的方式

使用同步容器类的方式很简单,如下代码就是使用了一个Map的同步类,对HashMap进行了包装。

//创建一个同步Map容器类,包装HashMap,使其线程安全
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<String, String>());

接下来的操作和平时使用HashMap没有什么区别,常用的方法都没有改变:

		//存储key-value
         synchronizedMap.put("hello","world");
         
        //通过key读取value
        String hello = synchronizedMap.get("hello");
        System.out.println(hello);
        
        //判断key存在不存在,不存在则put
        synchronizedMap.putIfAbsent("arong","java");

1.4、同步容器类的实现机制

同步容器类的实现机制前面已经说过,其实就是依靠synchronized块对方法进行了同步,以阻止多个线程对一个方法进行并发的访问,从而使其达到线程安全的效果,以下是Collections.synchronizedMap的相关代码,我们可以看出,每个方法都被上锁了:

//static静态化使用户通过Collections类可直接获取到它
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {

		//这一步返回一个SynchronizedMap类,并将Map作为参数传入
        return new SynchronizedMap<>(m);
    }

	//真正的同步容器类
    private static class SynchronizedMap<K,V>
        ...
		//所有的方法中都使用了synchronized对有状态变量进行了同步
        public int size() {
            synchronized (mutex) {return m.size();}
        }
        public boolean isEmpty() {
            synchronized (mutex) {return m.isEmpty();}
        }
        public boolean containsKey(Object key) {
            synchronized (mutex) {return m.containsKey(key);}
        }
        public boolean containsValue(Object value) {
            synchronized (mutex) {return m.containsValue(value);}
        }
        public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

        public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }
        public V remove(Object key) {
            synchronized (mutex) {return m.remove(key);}
        }
        public void putAll(Map<? extends K, ? extends V> map) {
            synchronized (mutex) {m.putAll(map);}
        }
        public void clear() {
            synchronized (mutex) {m.clear();}
        }

      ...

1.5、使用同步容器类的弊端

同步容器类虽然能保证在多线程请求的情况下容器数据的安全,但是弊端也是比较明显的:所有的方法都是同步的,每次只能有一个线程获得锁,其他线程全部阻塞,这样会导致性能问题,所以说,在对性能的要求不高,但是要求安全性良好的情况下,可以使用同步容器类,但是如果希望容器在并发请求时能同时接受多个线程的处理,并且也能保证线程安全的话,推荐使用下文介绍的并发容器类。

二、并发容器

2.1、什么是并发容器类

Java5.0通过提供几个并发的容器类来改进同步容器。同步容器通过对容器的所有状态进行串行访问。从而实现了它们的线程安全。这样做的代价是削减了并发性,当多个线程共同竞争容器级的锁时,吞吐量就会降低。另一方面,并发容器是为了多线程并发访问而设计的。Java5.0添加了ConcurentHashMap来替代同步的HashMap实现;当多数操作为读取操作时,CopyOnWriteArrayList是List相应的同步实现。新的ConcurrentMap接口还加入了常见复合操作,比如“缺少即加入(put-if-absent)”、替换和条件删除等。并发容器类位于java.util.concurrent,即并发包下

2.2、CAS算法

CAS是一种无锁的非阻塞算法,全称为:Compare-and-swap(比较并交换),大致思路是:先比较目标对象现值是否和旧值一致,如果一致,则更新对象为新值;如果不一致,则表明对象已经被其他线程修改,直接返回。

2.3、ConcurrentHashMap

ConcurrentHashMap实现了HashTable的所有功能,线程安全,但却在检索元素时不需要锁定,因此效率更高。ConcurrentHashMap的key 和 value都不允许null出现。原因在于ConcurrentHashMap不能区分出value是null还是没有map上,相对的HashMap却可以允许null值,在于其使用在单线程环境下,可以使用containKey(key)方法提前判定是否能map上,从而区分这两种情况,但是ConcurrentHashMap在多线程使用上下文中则不能这么判定。在并发编程中,常用来替换Collections.synchronizeHashMap

2.4、CopyOnWriteArrayList

CopyOnWriteArrayList提供高效地读取操作,使用在读多写少的场景。CopyOnWriteArrayList读取操作不用加锁,且是安全的;写操作时,先copy一份原有数据数组,再对复制数据进行写入操作,最后将复制数据替换原有数据,从而保证写操作不影响读操作。在并发编程中,常常用来替换Collections.synchronizedList

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/83351551