原子类
文章目录
1、原子类作用与介绍。
1.1特点
- 不可分离
- 一个操作中式不可中断的,即使是多线程的情况下也可以保证。
1.2作用
-
当某些变量需要保证线程安全的时候使用。
-
原子类的作用和锁类似,是为了保证并发情况下线程安全。不过原子类相比于锁,有一定的优势:
- 粒度更细:原子变量可以把竞争范围缩小到变量级别,这时我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度。
- 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况。
2、6类原子类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYPXC70s-1600216554785)(%E5%8E%9F%E5%AD%90%E7%B1%BB.assets/image-20200915172735035.png)]
2.1、Atomic*基本类型原子类
2.1.1AtomicInteger
常用方法
- public final int get();
- public final int getAndSet(int newValue)//获取当前值,并设定新的值
- public final int getAndIncrement();//获取当前值并自增。
- public final int getAndAdd(int data);//获取当前值并加上data。
- boolean compareAndSet(int expect,int newValue);//如果是期待的值就替换为newValue。
代码演示:
/**
* 描述: 演示AtomicInteger的基本用法,对比非原子类的线程安全问题,使用了原子类之后,不需要加锁,也可以保证线程安全。
*/
public class AtomicIntegerDemo1 implements Runnable {
private static final AtomicInteger atomicInteger = new AtomicInteger();
public void incrementAtomic() {
atomicInteger.getAndAdd(1);
}
private static volatile int basicCount = 0;
public void incrementBasic() {
basicCount++;
}
public static void main(String[] args) throws InterruptedException {
AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("原子类的结果:" + atomicInteger.get());
System.out.println("普通变量的结果:" + basicCount);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}
}
原子类的结果:20000
普通变量的结果:19672
- AtomicLong
- AtomicBoolean
2.1.2Atomic数组
代码演示
/**
* 描述: 演示原子数组的使用方法
*/
public class AtomicArrayDemo {
public static void main(String[] args) {
//创建长度为1000的数据,初始化为0-
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);
Incrementer incrementer = new Incrementer(atomicIntegerArray);
Decrementer decrementer = new Decrementer(atomicIntegerArray);
//建立两个包含100个线程的线程数组
Thread[] threadsIncrementer = new Thread[100];
Thread[] threadsDecrementer = new Thread[100];
for (int i = 0; i < 100; i++) {
threadsDecrementer[i] = new Thread(decrementer);
threadsIncrementer[i] = new Thread(incrementer);
threadsDecrementer[i].start();
threadsIncrementer[i].start();
}
// Thread.sleep(10000);
for (int i = 0; i < 100; i++) {
try {
threadsDecrementer[i].join();
threadsIncrementer[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//自加了一千次,自减了一千次。所以最后结果为0
for (int i = 0; i < atomicIntegerArray.length(); i++) {
// if (atomicIntegerArray.get(i)!=0) {
// System.out.println("发现了错误"+i);
// }
System.out.println(atomicIntegerArray.get(i));
}
System.out.println("运行结束");
}
}
class Decrementer implements Runnable {
private AtomicIntegerArray array;
public Decrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
//每个位置自减
array.getAndDecrement(i);
}
}
}
class Incrementer implements Runnable {
private AtomicIntegerArray array;
public Incrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndIncrement(i);
}
}
}
2.1.3 把普通变量升级为具有原子功能。
-
AtomicIntegerFieldUpdater对普通变量进行升级。
-
使用场景:偶尔需要一个原子get-set操作。
-
代码演示:
/** * 描述: 演示AtomicIntegerFieldUpdater的用法 */ public class AtomicIntegerFieldUpdaterDemo implements Runnable{ static Candidate tom; static Candidate peter; public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater .newUpdater(Candidate.class, "score"); @Override public void run() { for (int i = 0; i < 10000; i++) { peter.score++; scoreUpdater.getAndIncrement(tom); } } public static class Candidate { volatile int score; } public static void main(String[] args) throws InterruptedException { tom=new Candidate(); peter=new Candidate(); 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); } }
普通变量:18696
升级后的结果20000
3、Adder累加器
-
是Java8引入的,是一个比较新的类
-
高并发下LongAdder比AtomicLong效率高,本质上是空间换时间。
-
竞争激烈的时候,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。
-
AtomicLong与LongAdder 的代码演示。
/** * 描述: 演示高并发场景下,LongAdder比AtomicLong性能好 */ public class AtomicLongDemo { public static void main(String[] args) throws InterruptedException { 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)); } private 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(); } } } } /** * 描述: TODO 演示高并发场景下,LongAdder比AtomicLong性能好 */ public class LongAdderDemo { public static void main(String[] args) throws InterruptedException { 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)); } private 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(); } } } }
原因分析
-
由于竞争很激烈,每一次加法,都要进行flush和refresh,导致非常耗费资源。每一次加法都需要同步。
-
而LongAdder,每个线程都有一个自己的计数器,仅仅是用在自己线程内计数,这样一来就不会被其它线程的计数器干扰。最后再把所有的线程的计数器汇总,获得累加次数。
-
LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数:
-
base变量:竞争不激烈,直接累加到该变量上
-
Cell[]数组:竞争激烈,各个线程分散累加到自己的Cell[i]中。
-
sum源码分析。通过源码可知,在累加的过程中并没有添加锁,as[i]在加的过程中仍然有可能发生变化,仍是线程不安全的。
-
-