Atomic&Collections

1、Atomic

  原子操作,即不能被分割的最小粒子

 1.1 Atomiclnteger的使用场景

    AtomicInteger提供原子操作来进行Integer的使用,适合并发情况下的使用,比如两个线程对同一个整数累加。

 1.2 为什么Atomiclnteger是线程安全的,原理是什么

    AtomicInteger是对int类型的一个封装,提供原子性的访问和更新操作,其原子性操作的实现是基于CAS(compare-and-swap)技术。CAS表现为一组指令,当利用CAS执行试图进行一些更新操作时,会首先比较当前数值,如果数值未变,代表没有其它线程进行并发修改,则成功更新。如果数值改变,则可能出现不同的选择,要么进行重试,要么就返回是否成功。也就是所谓的“乐观锁”

 1.3 Atomiclnteger的CAS机制会导致什么问题

    会导致ABA问题,操作对象,获取对象后,执行CAS操作前,被其他线程修改后,且又修改为原来的对象值,导致CAS忽略其他线程的修改,成功执行CAS对象修改。 从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

 1.4 用volatile修饰变量不可以吗

    volatile只是保证能够从主内存中取到最新的值,但是不能保证并发的正确性

 

2、HashMap

  数据结构:数组+链表+(红黑树jdk>=1.8)

  重要成员变量:

    DEFAULT_INITIAL_CAPACITY = 1 << 4; Hash表默认初始容量
    MAXIMUM_CAPACITY = 1 << 30; 最大Hash表容量
    DEFAULT_LOAD_FACTOR = 0.75f;默认加载因子
    TREEIFY_THRESHOLD = 8;链表转红黑树阈值
    UNTREEIFY_THRESHOLD = 6;红黑树转链表阈值
    MIN_TREEIFY_CAPACITY = 64;链表转红黑树时hash表最小容量阈值,达不到优先扩容。
 
  jdk1.7及以下,高并发场景下,扩容可能产生死锁以及get操作可能会带来数据丢失。
  jdk1.8采用了高低位拆分转移的方式,对源码进行了优化,避免了链表环的产生。(并且引入了红黑树)
  

3、ConcurrentHashMap

  数据结构:数据结构与HashMap类似

  区别:① 内部在数据写入时加了同步机制保证线程安全,读操作是无锁操作

     ② 扩容时老数据的转移是并发执行的,这样效率更高

 

4、CopyOnWrite机制

  读写分离,空间换时间,避免为保证并发安全导致的激烈的锁的竞争。

  关键点:

    ① CopyOnWrite适用于读多写少的情况,最大程度的提高读的效率;

    ② CopyOnWrite是最终一致性,在写的过程中,原有读的数据是不会发生更新的,只有新的读才能读到新数据

    ③ 如果需要其他线程及时的读到数据,需要使用volatile变量

    ④ 写的时候不能并发写,需要对写操作进行加锁

  优点:

    对于一些读多写少的数据,写入时复制的做法就很不错,例如配置、黑名单、物流地址等变化非常少的数据,这是一种无锁的实现。可以帮我们实现程序更高的并发。

CopyOnWriteArrayList 并发安全且性能比 Vector 好。Vector 是增删改查方法都加了synchronized 来保证同步,但是每个方法执行的时候都要去获得锁,性能就会大大下降,而 CopyOnWriteArrayList 只是在增删改上加锁,但是读不加锁,在读方面的性能就好于 Vector。

  缺点:

    数据一致性问题。这种实现只是保证数据的最终一致性,在添加到拷贝数据而还没进行替换的时候,读到的仍然是旧数据。

内存占用问题。如果对象比较大,频繁地进行替换会消耗内存,从而引发 Java 的 GC 问题,这个时候,我们应该考虑其他的容器,例如 ConcurrentHashMap。

posted @ 2020-11-10 10:22  vvning  阅读(72)  评论(0编辑  收藏  举报