原子类
- 原子类
- 不可分割
- 一个操作是不可中断的,即使是多线程情况下也可以保证
- java.util.concurrent.atomic
- 原子类的作用和锁类似,是为了保证并发情况下线程安全、不过原子类比锁有一定得优势:
- 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度。
- 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况。
- AtomicInteger常用方法:
- getAndDecrement() 获取当前值,并自减
- getAndAdd(int delta) 获取当前值,并加上预期的值
- compareAndSet(int expect,int update) 如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)。
- get() 获取当前的值。
- getAndSet(int newValue) 获取当前值,并设置新值
- getAndIncrement() 获取当前值,并自增
/**
* 演示AtomicInteger的基本语法,对比非原子性的线程安全问题使用原子类后不需要加锁,也可以保证线程安全
*/
public class AtomicIntegerDemo1 implements Runnable {
private static final AtomicInteger atomicInteger = new AtomicInteger();
public void incrementAtomic() {
// atomicInteger.getAndIncrement();
atomicInteger.getAndAdd(-90);
}
private static volatile int basicCount = 0;
public void incrementBasic() {
basicCount++;
}
public static void main(String[] args) throws InterruptedException {
AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("原子类的结果:"+atomicInteger.get());
System.out.println("普通变量的结果:"+basicCount);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}
}
- Atomic*Reference引用类型原子类
- AtomicReference类的作用,和AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,AtomicReference可以让一个对象保证原子性,当然AtomicReference的功能明显比AtomicInteger强,因为对象包含很多属性,用法和AtomicInteger类似。
- AtomicIntegerFieldUpdater
- 实现了普通变量的原子操作
- 可见范围(元素需要volatile修饰,如下面代码,score没有volatile修饰,会报java.lang.IllegalArgumentException: Must be volatile type)
- 不支持static
/**
* 演示AtomicIntegerFieldUpdater用法
*/
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candicade tom;
static Candicade peter;
private static AtomicIntegerFieldUpdater<Candicade> scoreUpdate = AtomicIntegerFieldUpdater.newUpdater(Candicade.class,"score");
@Override
public void run() {
for(int i = 0;i<10000;i++){
peter.score ++;
scoreUpdate.getAndIncrement(tom);
}
}
public static class Candicade{
volatile int score;
}
public static void main(String[] args) throws InterruptedException {
tom = new Candicade();
peter = new Candicade();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量"+peter.score);
System.out.println("升级后的变量"+tom.score);
}
}
* Adder累加器
- java8引入的,相对比较新的一个类。
- 高并发下LongAdder比AtomicLong效率高,不过本质都是空间换时间。
- 竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。
LongAdder代码演示,后面有AtomicLong代码演示
/**
* 演示高并发情景下,LongAdder比AtomicLong性能好
*/
public class LongAdderDemo {
public static void main(String[] args) {
LongAdder counter = new LongAdder();
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for(int i = 0 ; i<10000 ; i++){
service.submit(new Task(counter));
}
service.shutdown();
while (!service.isTerminated()){
}
long end = System.currentTimeMillis();
System.out.println(counter.sum());
System.out.println("LongAdder耗时:"+(end - start));
}
public static class Task implements Runnable{
private LongAdder counter;
public Task(LongAdder counter) {
this.counter = counter;
}
@Override
public void run() {
for(int i = 0 ; i<10000 ; i++){
counter.increment();
}
}
}
}
AtomicLong代码演示
/**
* 演示高并发情景下,LongAdder比AtomicLong性能好
*/
public class AtomicLongDemo{
public static void main(String[] args) {
AtomicLong counter = new AtomicLong(0);
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for(int i = 0 ; i<10000 ; i++){
service.submit(new Task(counter));
}
service.shutdown();
while (!service.isTerminated()){
}
long end = System.currentTimeMillis();
System.out.println(counter.get());
System.out.println("AtomicLong耗时:"+(end - start));
}
public static class Task implements Runnable{
private AtomicLong counter;
public Task(AtomicLong counter) {
this.counter = counter;
}
@Override
public void run() {
for(int i = 0 ; i<10000 ; i++){
counter.incrementAndGet();
}
}
}
}
AtomicLong由于竞争激烈,每次加法,都要flush和refresh,导致很耗费资源。
- LongAdder的改进和原理:
- 在内部,两个实现原理是不同的,上面的AtomicLong实现原理是,每一次加法都需要做同步,所以在高并发的时候就会导致冲突比较多,也就降低了效率。
- 而此时的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内部计数,这样一来就不会和其他线程计数器干扰。
- 如下图所示,两个ctr各自加各自的,没有竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要flush和refresh,这里也没有一个公共的counter来给所有线程统一计数。
- LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数:
- base变量 :竞争不激烈,直接累加到该变量上。
- Cell[]数组:竞争激烈,各个线程分散累加到自己的槽Cell[i]中。
- sum源码分析
- AtomicLong和LongAdder对比:
- 在低争用下,两个具有相似的特征。但是竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但要消耗的空间也更多。
- LongAdder适合的场景是统计求和计数的场景,而AtomicLong还具有CAS方法。
* Accumulator累加器
- Accumulator和Adder非常相似,Accumulator就是一个更通用版本的Adder。
/**
* 演示LongAccumulator
*/
public class LongAccumulatorDemo {
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator(((x, y) -> Math.max(x, y)), 0);
// accumulator.accumulate(1);
// accumulator.accumulate(3);
ExecutorService executorService = Executors.newFixedThreadPool(8);
IntStream.range(0, 10).forEach(i -> executorService.submit(() -> accumulator.accumulate(i)));
executorService.shutdown();
while (!executorService.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
}