什么是原子类
- 不可分割
- 一个操作是不可中断的,即便是多线程的情况下也是可以保证的
- java.util.concurrent.atomic包下
- 原子类的作用和锁类似,是为了保证并发情况下线程安全,原子类相比于锁,有一定的优势
- 粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得最细粒度的情况了
- 性能更高:通常情况下使用原子类的效率比使用锁的效率更高,除了高度竞争的情况
Atomic*基本原子类
AtomicInteger为例
- public final int get():获取当前值
- public final int getAndSet(int newValue):获取当前值然后设置新值
- public final int getAndIncrement():获取当前值然后自增
- public final int getAndDecrement():获取当前值然后自减
- public final int getAndAdd(int delta):获取当前值然后加上参数中的值
- public final int compareAndSet(int expect,int update):如果当前数值等于预期值,则以原子方式将该值设置进去
原子类和非原子类对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| private static final AtomicInteger atomicInteger = new AtomicInteger();
public void incrementAtomic() { atomicInteger.getAndIncrement(); }
private static volatile int basicCount = 0;
public synchronized void incrementBasic() { basicCount++; }
@Override public void run() { for (int i = 0; i < 10000; i++) { incrementAtomic(); incrementBasic(); } }
public static void main(String[] args) throws InterruptedException { AtomicIntergerDemo atomicIntergerDemo = new AtomicIntergerDemo(); Thread thread1 = new Thread(atomicIntergerDemo); Thread thread2 = new Thread(atomicIntergerDemo); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("原子类->"+atomicInteger.get()); System.out.println("非原子类->"+basicCount);
}
|
如果非原子类不加锁就不能保证正确的结果
AtomicArray基本原子类
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| public class AtomicArrayDemo {
public static void main(String[] args) throws InterruptedException { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);
Thread[] threadsIncrementer = new Thread[100]; Thread[] threadsDecrementer = new Thread[100];
Incrementer incrementer = new Incrementer(atomicIntegerArray); Decrementer decrementer = new Decrementer(atomicIntegerArray);
for (int i = 0; i < 100; i++) { threadsDecrementer[i] = new Thread(decrementer); threadsIncrementer[i] = new Thread(incrementer); threadsDecrementer[i].start(); threadsIncrementer[i].start(); }
for (int i = 0; i < 100; i++) { threadsDecrementer[i].join(); threadsIncrementer[i].join(); }
for (int i = 0; i < atomicIntegerArray.length(); i++) { if (atomicIntegerArray.get(i)!= 0) { System.out.println("发现了错误-" + 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); } } }
|
Atomic*Reference引用类型原子类
和AtomicInteger没有本质区别,AtomicReference可以让一个对象保证原子性,用法和AtomicInteger类似
把普通变量升级为具有原子功能
- AtomicIntegerFieldUpdater对普通变量进行升级
- 使用场景:偶尔需要一个原子get-set操作
升级后与普通变量的对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class AtomicIntegerFieldUpdaterDemo implements Runnable {
static Candidate aa; static Candidate bb;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
@Override public void run() { for (int i = 0; i < 10000; i++) { aa.score++; scoreUpdater.getAndIncrement(bb); } }
public static class Candidate { volatile int score; }
public static void main(String[] args) throws InterruptedException { aa = new Candidate(); bb = new Candidate();
AtomicIntegerFieldUpdaterDemo updaterDemo = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(updaterDemo); Thread t2 = new Thread(updaterDemo);
t1.start(); t2.start();
t1.join(); t2.join(); System.out.println("普通->" + aa.score); System.out.println("升级的->" + bb.score); } }
|
注意点:
- 必须要是可见的,private是不行的
- 不支持static
Adder累加器
- java8引入的,相对较新
- 高并发下LongAdder比AtomicLong效率高,本质是空间换时间
- 竞争激烈的时候,LongAdder把不同线程对应到不用的Cell上进行修改,降低了冲突的概率,是多段锁的概念,提高了并发性
AtomicLong代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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("耗时->"+(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.getAndIncrement(); } } } }
|
LongAdder代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| 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("耗时->"+(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(); } } } }
|
最后根据打印结果可以看出LongAdder耗时比AtomicLong少得多
LongAdder带来的改进和原理
- AtomicLong的每一次加法都需要做同步,所以在高并发的时候导致冲突比较多,降低了效率
- LongAdder每一个线程会有自己的一个计数器,仅用来在自己线程计数,就不会和其他线程计数器有干扰
- LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数
- base变量:竞争不激烈,直接累加到该变量上
- cell[]数组:竞争激烈,各个线程分散累加到自己的Cell[i]中
- sum就是把base和每一个cell加起来
对比
- 在低争用的情况下,两个类具有相似的特征,但竞争激烈的情况下,LongAdder的吞吐量要高得多,但消耗更多空间
- LongAdder只有add方法,所以只适合统计求和计数,而AtomicLong还具有cas方法