多线程环境下如何安全的使用线性表, 队列, 哈希表

小王学习录

  • 今日鸡汤
  • 安全使用ArrayList
  • 安全使用队列
  • 安全使用HashMap

今日鸡汤

安全使用ArrayList

  1. 使用synchronized锁或者reentrantLock锁
  2. 使用CopyOnWriteArrayList(COW写时拷贝)类来代替ArrayList类.
  1. 多个线程对CopyOnWriteArrayList里面的ArrayList进行读操作, 不会发生线程安全问题, 不做任何处理
  2. 多个线程对CopyOnWriteArrayList里面的ArrayList进行写操作, 会为每个线程创建一个副本, 各个线程在各自独立的副本上进行修改, 最后不会进行汇总, 最后一个完成写操作的副本将会代替旧的ArrayList(牺牲了实时性来确保线程安全). 如果在写操作期间有线程对其进行读操作, 会读旧的(修改之前的)ArrayList. 本质上是引用之间的赋值. 这种方法适用于读频繁, 不经常写入的情况. 如服务器的配置文件.
  3. CopyOnWriteArrayList的局限性在于:
    (1) 牺牲了实时性, 同时多个线程进行写操作时之会将最后一个完成写操作的副本代替原来的ArrayList, 而率先完成写操作的副本将会作废, 也因此这种方法只适用于读频繁而不经常写的情况
    (2) 这种方法也只适用于ArrayList较小的情况, 否则在创建副本时将会付出巨大的开销.
    (3) 服务器的配置文件就是采用这种写时拷贝的方式来对配置文件进行修改的, 这个功能叫热加载

安全使用队列

  1. 基于数组实现的阻塞队列
ArrayBlockingQueue
  1. 基于链表实现的阻塞队列
LinkedBlockingQueue
  1. 基于堆实现的带优先级的阻塞队列
PriorityBlockingQueue
  1. 最多只包含一个元素的阻塞队列
TransferQueue

安全使用HashMap

  1. 使用HasHTable代替HashMap, HashTable中的关键方法进行了上锁, 使用时是线程安全的
  2. 使用ConcurrentHashMap代替HashMap

ConcurrentHashMap和HashTable的区别:

  1. ConcurrentHashMap的锁粒度更小, 锁冲突发生的概率小.
    HashTable的锁对象是整个哈希表, 这就意味着对哈希表中的任何元素进行操作都会上锁, 但是有些操作是不需要上锁的.
    多线程环境下如何安全的使用线性表, 队列, 哈希表_第1张图片而ConcurrentHashMap的锁对象是单个链表, 这就大大细化了锁的粒度, 大大减小了发生锁冲突的概率, 且提高了性能
    多线程环境下如何安全的使用线性表, 队列, 哈希表_第2张图片
  2. ConcurrentHashMap对读操作不上锁, 只对写操作上锁. 通过volatile关键字+原子操作写来确保边读边写的线程安全, 避免脏读的发生.
  3. ConcurrentHashMap使用了CAS操作, 尽量减少上锁的操作. 提高并发性能.
  4. 扩容方面, ConcurrentHampMap使用了化整为零的方式.
    (1) HashTable在进行扩容时, 容量扩为原来的两倍, 将原哈希表中的数据复制到新表中, 因为其是对整个哈希表上锁, 所以在整个哈希表未完成扩容时, 对其的读写操作都会陷入阻塞, 这大大降低了性能
    (2) ConcurrentHashMap在扩容时, 由于锁对象是链表, 所以在进行数据迁移时, 各个链表可以独立迁移, 这样在一次迁移中只对正在迁移的链表上锁, 针对其他链表的读写操作还可以执行.
    如果要执行put操作, 则直接在新的哈希表中添加
    如果要执行take操作, 则对原哈希表和新哈希表都进行检索
    直到新哈希表扩容完成(数据全部迁移), 再释放旧的哈希表.

你可能感兴趣的:(操作系统,安全,散列表,数据结构,操作系统,多线程,线程安全,java)