Java并发编程(4)-同步容器和并发容器

释放双眼,带上耳机,听听看~!

文章目录

  • 一、同步容器

  • 1.1、同步容器类介绍
    * 1.2、常用的同步容器类
    * 1.3、使用同步容器类的方式
    * 1.4、同步容器类的实现机制
    * 1.5、使用同步容器类的弊端

    
    
    1
    2
    1  * 二、并发容器
    2
  • 2.1、什么是并发容器类
    * 2.2、CAS算法
    * 2.3、ConcurrentHashMap
    * 2.4、CopyOnWriteArrayList



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进行了包装。


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

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


1
2
3
4
5
6
7
8
9
10
11
1       //存储key-value
2         synchronizedMap.put("hello","world");
3        
4        //通过key读取value
5        String hello = synchronizedMap.get("hello");
6        System.out.println(hello);
7        
8        //判断key存在不存在,不存在则put
9        synchronizedMap.putIfAbsent("arong","java");
10
11

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1//static静态化使用户通过Collections类可直接获取到它
2public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
3
4       //这一步返回一个SynchronizedMap类,并将Map作为参数传入
5        return new SynchronizedMap<>(m);
6    }
7
8   //真正的同步容器类
9    private static class SynchronizedMap<K,V>
10        ...
11      //所有的方法中都使用了synchronized对有状态变量进行了同步
12        public int size() {
13            synchronized (mutex) {return m.size();}
14        }
15        public boolean isEmpty() {
16            synchronized (mutex) {return m.isEmpty();}
17        }
18        public boolean containsKey(Object key) {
19            synchronized (mutex) {return m.containsKey(key);}
20        }
21        public boolean containsValue(Object value) {
22            synchronized (mutex) {return m.containsValue(value);}
23        }
24        public V get(Object key) {
25            synchronized (mutex) {return m.get(key);}
26        }
27
28        public V put(K key, V value) {
29            synchronized (mutex) {return m.put(key, value);}
30        }
31        public V remove(Object key) {
32            synchronized (mutex) {return m.remove(key);}
33        }
34        public void putAll(Map<? extends K, ? extends V> map) {
35            synchronized (mutex) {m.putAll(map);}
36        }
37        public void clear() {
38            synchronized (mutex) {m.clear();}
39        }
40
41      ...
42
43

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

给TA打赏
共{{data.count}}人
人已打赏
安全技术

用node.js从零开始去写一个简单的爬虫

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索