原子的大概意思就是不可分割,在程序中的表现形式为一个操做 不可中断,即使是多线程的状况下也能够保证,其中在Java
中就有java.util.concurrent.atomic
包,这个包用来存储原子类。
原子类的做用与锁相似,都是为了在并发场景下保证 线程安全,不过原子类相对于锁具备如下优点:
CAS
思想的体现。/** * 描述: 演示 AtomicInteger 的基本用法,而且对比非原子类的线程安全问题, * 使用了原子类以后,不须要加锁也能够保证线程安全。 */ public class AtomicIntegerDemo1 implements Runnable { private static final AtomicInteger atomicInteger = new AtomicInteger(); public void incrementAtomic() { atomicInteger.getAndIncrement(); } // 添加 volatile 关键字保证可见性的同时不保证原子性 private static volatile int basicCount = 0; public void incrementBasic() { basicCount++; } @Override public void run() { for (int i = 0; i < 10000; i++) { incrementAtomic(); incrementBasic(); } } public static void main(String[] args) throws InterruptedException { AtomicIntegerDemo1 demo1 = new AtomicIntegerDemo1(); Thread t1 = new Thread(demo1); Thread t2 = new Thread(demo1); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("原子类的结果是:" + atomicInteger.get()); System.out.println("普通变量的结果是:" + basicCount); } }
运行结果:java
/** * 描述: 演示原子数组的使用方法 */ public class AtomicArrayDemo { public static void main(String[] args) throws InterruptedException { AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000); Decrementer decrementer = new Decrementer(atomicIntegerArray); Incrementer incrementer = new Incrementer(atomicIntegerArray); 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(); } 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("发现了非0值" + 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); } } }
运行结果:数组
由于使用的是原子数组,因此不管运行多少次都不会出现非0的状况。安全
AtomicReference
类的做用和AtomicInteger
并无什么区别,AtomicInteger
能够 让一个整数保证原子性,而AtomicReference
可让一个 对象保证原子性。由于对象中能够包含多个属性,因此AtomicReference
比AtomicInteger
功能要强一些。
/** * 描述: 演示自旋锁 */ public class SpinLock { // 声明自旋锁 private AtomicReference<Thread> sign = new AtomicReference<>(); /** * 加锁 */ public void lock() { Thread current = Thread.currentThread(); // 使用 while 循环加 CAS 操做实现自旋 // 但愿没有线程持有锁,传入 null // 但愿更新的值是本身,传入 current while (!sign.compareAndSet(null, current)) { System.out.println(Thread.currentThread().getName() + "自旋获取失败,再次尝试"); } } /** * 解锁 */ public void unlock() { Thread current = Thread.currentThread(); // 解锁首先要有锁,因此一次就能够解掉,不须要 while 循环 // 但愿持有锁的线程是本身,传入 current // 由于是解锁,因此要更新的值为 null sign.compareAndSet(current, null); } public static void main(String[] args) { SpinLock spinLock = new SpinLock(); Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始尝试获取自旋锁"); spinLock.lock(); System.out.println(Thread.currentThread().getName() + "获取到了自旋锁"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLock.unlock(); System.out.println(Thread.currentThread().getName() + "释放了自旋锁"); } } }; // 用两个线程执行任务,模拟争抢 Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable); thread1.start(); thread2.start(); } }
原子变量在保证线程安全的同时开销也要比普通变量大得多,由于须要维护内部的一些逻辑,因此若是在须要的时候将普通变量升级为原子变量的话,就能够大大的节省系统资源,提高系统执行效率,下面就来演示一下将普通变量升级为原子变量。
/** * 描述: 演示 FieldUpdater 的用法 */ 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 将普通变量升级为原子变量 scoreUpdater.getAndIncrement(tom); } } public static class Candidate { volatile int score; } public static void main(String[] args) throws InterruptedException { AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo(); tom = new Candidate(); peter = new Candidate(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); t1.start(); t2.start(); t1.join(); t1.join(); System.out.println("普通的变量: " + peter.score); System.out.println("升级后的变量: " + tom.score); } }
运行结果:多线程
int
类型的字段,int
的包装类也不能够volatile
修饰private
访问权限static
关键字因为long
的字节数比int
要多,因此在使用AtomicLong
时的效率要比AtomicInteger
要低一些,针对这个状况,在 Java 8 中引入了一个新的类 Adder累加器,在高并发场景下LongAdder
的开销要比AtomicLong
效率要高,不过本质是 以空间换时间。当竞争激烈的时候,LongAdder
把不一样线程对应到不一样的Cell
上进行修改,下降了冲突的几率,这也是 多段锁的理念,从而提高了并发性。
/** * 描述: 演示高并发场景下 LongAdder 要比 AtomicLong 要好。 */ public class AtomicLongDemo { public static void main(String[] args) throws InterruptedException { AtomicLong counter = new AtomicLong(0); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println(counter.get()); System.out.println("AtomicLong 耗时为:" + (endTime - startTime) + " ms"); } 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(); } } } }
运行结果:并发
/** * 描述: 演示高并发场景下 LongAdder 要比 AtomicLong 要好。 */ public class LongAdderDemo { public static void main(String[] args) throws InterruptedException { LongAdder counter = new LongAdder(); ExecutorService executorService = Executors.newFixedThreadPool(20); long startTime = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { executorService.submit(new Task(counter)); } executorService.shutdown(); while (!executorService.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println(counter.sum()); System.out.println("LongAdder 耗时为:" + (endTime - startTime) + " ms"); } 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(); } } } }
运行结果:ide
在使用AtomicLong
进行累加时,每一个线程之间须要将使用共享内存进行通讯,即线程执行操做时只能在工做内存中进行,而后将值刷新到主内存,在高并发场景下频繁的刷新和获取操做会带来必定的性能开销。高并发
LongAdderLongAdder
引入了分段累加的概念,在内部有一个base
变量和Cell[]
数组共同参与计数:工具
Cell[i]
中。public long sum() { Cell[] as = cells; Cell a; long sum = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
首先将负责计数的cells
赋值给as
,这样就能够经过as
来获取数值了,base
用于并发不激烈的状况下,这里使用了sum
进行统计。接下来是判断as
的是否为空,若是为空说明根本没有向Cell
中存储数,因此直接返回sum
(base)便可,若是用到了cell
就将它作遍历,并将值从中取出,并用sum
进行统计。源码分析
总结:在低争用下,AtomicLong
和LongAdder
这两个类具备相同的特性。可是在竞争激烈的状况下,LongAdder
的预期吞吐量要高得多,可是消耗的空间也多。性能
LongAdder
适合的场景是统计求和计数的场景,并且LongAdder
基本只提供add()
方法,而AtomicLong
还具备CAS
方法。
Accumulator
和Adder
很是类似,Accumulator
是一个更通用版的Adder
。
/** * 描述: 演示 LongAccumulator 的用法 */ public class LongAccumulatorDemo { public static void main(String[] args) { LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0); ExecutorService executorService = Executors.newFixedThreadPool(8); // 从0开始,从1加到9 IntStream.range(1, 10) .forEach(i -> executorService.submit(() -> longAccumulator.accumulate(i))); executorService.shutdown(); while (!executorService.isTerminated()) { } System.out.println(longAccumulator.getThenReset()); } }
运行结果:
参考连接:
慕课网之玩转Java并发工具,精通JUC,成为并发多面手