1、AtomicInteger、AtomicLong、AtomicBoolean等等
这几个类可以实现多线程之间的原子性递增或递减或取值操作,原理一个实例拥有一个原子变量,然后进行CAS操作。
2、LongAdder
然而AtomicLong等类存在一个主要问题就是,在高并发下大量线程会同时去竞争更新同一个原子变量,但由于同时只有一个线程的CAS操作会成功,导致其他线程竞争失败后,会无限循环地进行自旋尝试CAS操作,造成CPU资源的浪费。因此JDK8新增了一个原子性递增或者递减的LongAdder类,来克服AtomicLong上述缺点。解决的思路是尽量让一个线程拥有一个原子变量,这样就不用竞争了。
LongAdder类add()方法思路大致如下
1、用Cells数组存储2的N次方个原子变量(每个变量称为cell),然后让线程更具自己的“threadLocalRandomProbe”变量值去获取其中的元素,尽量每个线程都能获取到空的数组元素,为此在每次进行元素的cas操作失败的时候,会进行获取cell的优化处理;
2、获取到cell后,会进行CAS操作,假如失败会再尝试一次,还是失败就会进行cells扩容,每次扩容为上一次的两倍(左移操作),当cells数组个数大于可用的处理器个数时便不再扩容;然后再去获取cell,直到CAS操作成功
也就是说LongAdder利用cpu能同时处理的最大线程数,加快了CAS操作速度,而且每个线程都存储着自己获取cell的方式。
3、LongAccumulator
LongAdder 类是LongAccumulator 的一个特例, LongAccumul ator 比LongAdder 的功能更强大。例如下面的构造函数, 其中accumulatorFunction 是一个双目运算器接口, 其根据输入的两个参数返回一个计算值, identity 则是LongAccumulator 累加器的初始值。
public LongAccumulator (LongBinaryOperator accumulator Function ,long identity ) {
this.function = accumulatorFunction ;
base = this.identity= identity;
public interface LongBinaryOperator {
//根据两个参数计算并返回一个值
long applyAsLong ( long left , long right ) ;
}
上面提到, LongAdder 其实是LongAccumulator 的一个特例, 调用LongAdder 就相当于使用下面的方式调用LongAccumulator :
LongAdder adder= new LongAdder () ;
LongAccumulator accumulator= new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left , long right ) {
return left + right ;
}
},0);
4、CopyOnWirteArrayList
并发包中的List只有CopyOnwirteArrayList,此类是线程安全的ArrayList,但其存在弱一致性。所谓弱一致性就是说数据更新后,后续的访问只能访问到部分或者全部访问不到。
其原理大致为:
1、利用volatile修饰array数组,进行增删改操作时,用ReentrantLock获取lock再操作
之所以存在弱一致性,因为在使用iterator()遍历时,原数组已经修改了,但如果在修改前便已经获取了其iterator,iterator包含数组的final修饰的复制版本,后面数据修改并不会同步到复制版本,导致后面进行遍历时,仍然是修改前的数据。
所以应该等线程跑完了,再获取iterator。
----------------------------------------以下更新于2021年7月21日 06:49:07-----------------------------------------
1、ThreadLocalRandom类,让每个线程都持有一个本地的种子变量,该种子变量只有在使用随机数时才会被 初始化
ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
int random = threadLocalRandom.nextInt();
在多线程 下使用单个Random 实例生成随机数时,当多个线程同时计算随机数来计算新的种子 时, 多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS 操作,同 时只有一个线程会成功,所以会造成大量线程进行自旋重试, 这会降低并发性能,所以 ThreadLocalRandom 应运而生。
PS:Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
2、AtomicInteger、AtomicLong:递增、递减操作,原理是使用CAS提供非阻塞原子性操作,竞争获取原子变量。java 8 后新增LongAdder类,原理是将原子变量分成多个,再合并。
LongAdder是LongAccumulator的特例,LongAccumulator提供更强大的自定义累加规则
3、AtomicBoolean,布尔型的线程安全类。boolean线程不安全,boolean其实包含了boolean.false和boolean.true两个对象。AtomicBoolean将boolean型的true、false转成1和0,再使用CAS方法提供非阻塞原子性操作
4、SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
注意线程安全,使用DateUtils。亦推荐如下处理:
private static final ThreadLocal df = new ThreadLocal() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明:如果是JDK8的应用,可以使用Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。