一、ThreadLocalRandom
在多线程下使用单个Random实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新 *** 作,由于原子 *** 作的更新是`CAS` *** 作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,会降低并发性能。于是产生了`ThreadLoaclRandom`。
先简单说明下ThreadLocal的原理:ThreadLocal通过让每一个线程复制一份变量,使得在每一个线程对变量进行 *** 作的时实际上是 *** 作自己本地内存的里面的副本,从而避免了对共享变量进行同步(要同步就一定需要加锁,Thread实现的是每个线程访问的都是自己的副本,这样就不需要进行同步处理,也就不需要加锁了)。
ThreadLocalRandom使用ThreadLocal的原理,让每个线程都持有一个本地的种子变量,该种子变量只有在使用随机数时才会被初始化。在多线程下计算新种子是根据自己线程内维护的种子变量进行更新,从而避免了竞争。
二、原子 *** 作类(以AtomicLong为例)
AtomicLong是原子性递增或者递减类,内部使用Unsafe来实现。在没有原子类的情况下,实现计数器需要使用一定的同步措施,比如使用synchronized关键字等,由于这些都是阻塞算法,会对性能产生损耗。而AtomicLong等原子类使用CAS算法,对性能更好。但是在高并发情况下,大量线程会同时去竞争更新同一个原子变量,但由于只有一个线程的CAS *** 作会成功,大量的线程会竞争失败,通过无限循环不断进行自选尝试CAS的 *** 作,会白白浪费CPU的资源。
三、JDK8新增的原子 *** 作类LongAdder
由于AtomicLong在高并发下会导致CPU的白白浪费的问题,所以引入了LongAdder。它的思路就是把一个变量分解成多个变量,让同样多的线程去竞争多个资源。这样在同等并发量的情况下,争夺单个变量更新 *** 作的线程量就会减少,变相的减少了争夺共享资源的并发量。此外,要是多个线程竞争失败了,不会一直自旋尝试CAS,而是尝试在其他cell的变量上进行CAS尝试。最后再获得LongAdder当前值后,把所有cell变量的value值累加后再加上base返回。
四、CopyOnWriteArrayList(并发List包只有这一个)
CopyOnWriteArrayList是线程安全的ArrayList,对其进行的 *** 作都是在底层的一个复制的数组(快照)上进行的,也就是使用了写时复制策略。CopyOnWriteArrayList使用写时复制策略来保证list的一致性,而获取--修改--写入都不是原子性的,所以需要在增删改的过程中使用独占锁(一般使用ReentrantLock独占锁) ,来保证在某个时间只有一个线程对list数组进行修改。
此外CopyOnWriteArrayList提供弱一致性的迭代器,保证在获取迭代器后,其他线程对list数组的 *** 作是不可见的,迭代器遍历的数组是一个快照。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)