多线程进阶——线程安全的集合类

目录

一、多线程环境使用ArrayList

(一)自行加锁

(二)Collections.synchronizedList(new ArrayList);

(三)使用 CopyOnWriteArrayList

二、多线程环境使用哈希表

(一)Hashtable

(二)ConcurrentHashMap


原来的集合类,大部分都不是线程安全的。

Vector, Stack, HashTable, 是线程安全的(不建议用),其他集合类不是线程安全的。

一、多线程环境使用ArrayList

(一)自行加锁

具体代码具体分析,要把那些代码打包到一起,成为一个“原子”操作。

(二)Collections.synchronizedList(new ArrayList);

synchronizedList 是标准库提供的一个基于 synchronized 进行线程同步的 List. synchronizedList

的关键操作上都带有 synchronized,从而实现原子类。

这是不推荐的,加锁有代价。

(三)使用 CopyOnWriteArrayList

CopyOnWrite容器即写时复制的容器,使用 CopyOnWriteArrayList就不用去加锁,运用到了“写时拷贝”的思想方法。

它的工作原理是:

  • 当我们往一个容器中添加元素时,不直接往这个容器里面添加,而是先将当前容器进行copy,复制出一个新的容器,然后往新的容器里面添加元素。
  • 添加完元素之后,再讲原容器的引用指向新的容器。

多线程进阶——线程安全的集合类_第1张图片 

在多线程读取的时候,一旦某个线程进行写操作,比如修改 1 -> 100  
复制过程中,如果其他线程在读,就直接读取旧版本的数据  
虽然复制过程不是原子的(消耗一定的时间)  
由于提供了旧版本的数据,不影响其他线程读取  
新版本数组复制完毕之后,直接进行引用的修改。  
引用的赋值是“原子” 的,确保读取过程中,要么读到的是旧版数据,要么读到的是新版数据,不会读到“修改一半”的数据。 

二、多线程环境使用哈希表

HashMap本身不是线程安全的。

在多线程环境在使用哈希表可以使用:

  • Hashtable
  • ConcurrentHashMap

(一)Hashtable

Hashtable类中的各种public方法都加上了synchronized,这也相当于直接针对Hashtable对象本身加锁。

  • 如果多线程访问同一个 Hashtable 就会直接造成锁冲突.
  • size 属性也是通过 synchronized 来控制同步,也是比较慢的.
  • 一旦触发扩容,就由该线程完成整个扩容过程。这个过程会涉及到大量的元素拷贝,效率会非常低.

多线程进阶——线程安全的集合类_第2张图片

一个Hashtable只有一把锁,两个线程访问Hashtable中的任意数据都会出现锁竞争。

(二)ConcurrentHashMap

ConcurrHashMap根据Hashtable做出了一系列的改进和优化。

  • 读操作没有加锁,使用了volatile确保从内存中读取数据,只针对写操作进行加锁,但不是锁整个对象,而是“锁桶”:用每个链表的头结点作为锁对象,大大降低了锁冲突的概率。
  • 充分利用 CAS 特性。比如 size 属性通过 CAS 来更新。避免出现重量级锁的情况.
  • 优化了扩容方式:化整为零:

化整为零:

发现需要扩容的线程,只需要创建一个新的数组,同时只搬几个元素过去.
扩容期间,新老数组同时存在.
后续每个来操作 ConcurrentHashMap 的线程,都会参与搬家的过程。每个操作负责搬运一小部分元素.
搬完最后一个元素再把老数组删掉.
这个期间,插入只往新数组加.
这个期间。查找需要同时查新数组和老数组

 多线程进阶——线程安全的集合类_第3张图片

ConcurrentHashMap每个哈希桶都有一把锁,只有两个线程同时访问同一个哈希桶上的数据才会出现锁竞争。

你可能感兴趣的:(java,开发语言)