在当今科技高速发展的时代,计算机的高速发展早已超越“摩尔定律”。在这个计算机相对廉价的时代,做为开发者操做的机器早已不是单核处理器了,早已进入多核时代,业务早已进入并行执行;开发高并发的程序所要掌握的技能也再也不是使用没有效率的锁,所幸jdk1.5提供在多线程状况下无锁的进行原子操做,也就是这篇文章将要写的内容。java
自JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操做。原子变量的底层使用了处理器提供的原子指令,可是不一样的CPU架构可能提供的原子指令不同,也有可能须要某种形式的内部锁,因此该方法不能绝对保证线程不被阻塞。- 总的来讲就是提供非阻塞的线程安全编程node
在介绍用法前先了解jdk软件包 java.util.concurrent.atomic 中为咱们提供了哪些原子类和方法: 程序员
(1)类摘要 编程
类 | 描述 |
---|---|
AtomicBoolean | 能够用原子方式更新的 boolean 值。 |
AtomicInteger | 能够用原子方式更新的 int 值。 |
AtomicIntegerArray | 能够用原子方式更新其元素的 int 数组。 |
AtomicIntegerFieldUpdater | 基于反射的实用工具,能够对指定类的指定 volatile int 字段进行原子更新。 |
AtomicLong | 能够用原子方式更新的 long 值。 |
AtomicLongArray | 能够用原子方式更新其元素的 long 数组。 |
AtomicLongFieldUpdater | 基于反射的实用工具,能够对指定类的指定 volatile long 字段进行原子更新。 |
AtomicMarkableReference | AtomicMarkableReference 维护带有标记位的对象引用,能够原子方式对其进行更新。 |
AtomicReference | 能够用原子方式更新的对象引用。 |
AtomicReferenceArray | 能够用原子方式更新其元素的对象引用数组。 |
AtomicReferenceFieldUpdater | 基于反射的实用工具,能够对指定类的指定 volatile 字段进行原子更新。 |
AtomicStampedReference | AtomicStampedReference 维护带有整数“标志”的对象引用,能够用原子方式对其进行更新。 |
(2)经常使用方法摘要api
返回类型 | 方法 | 描述 |
---|---|---|
boolean | compareAndSet(boolean expect, boolean update) | 若是当前值 == 预期值,则以原子方式将该值设置为给定的更新值 |
boolean | get() | 返回当前值。 |
void | set(boolean newValue) | 无条件地设置为给定值。 |
boolean | weakCompareAndSet(boolean expect, boolean update) | 若是当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 |
这里介绍只列出经常使用的方法,实际中据原子类不一样方法略有变化。如需了解更多的方法请查看“在线文档-jdk-z”数组
(3)简单使用示例 bash
示例一:原子更新基本类型类--生成序列号数据结构
public class Example1 {
private final AtomicLong sequenceNumber = new AtomicLong(0);
public long next() {
//原子增量方法,执行的是i++,因此须要在获取一次。
sequenceNumber.getAndIncrement();
return sequenceNumber.get();
}
public void radixNext(int radix){
for (;;) {
long i = sequenceNumber.get();
// 该方法不必定执行成功,因此用无限循环来保证操做始终会执行成功一次。
boolean suc = sequenceNumber.compareAndSet(i, i + radix);
if (suc) {
break;
}
}
}
public static void main(String[] args) {
Example1 sequencer = new Example1();
//生成序列号
for (int i = 0; i < 10; i++) {
System.out.println(sequencer.next());
}
//生成自定义序列号
for (int i = 0; i < 10; i++) {
sequencer.radixNext(3);
System.out.println(sequencer.sequenceNumber.get());
}
}
}复制代码
执行结果:多线程
1
2
3
4
5
---------------
8
11
14
17
20复制代码
示例二:原子方式更新数组
public class Example2 {
static AtomicIntegerArray arr = new AtomicIntegerArray(10);
public static class AddThread implements Runnable{
public void run(){
for(int k=0;k<10000;k++){
// 以原子方式将索引 i 的元素加 1。
arr.getAndIncrement(k%arr.length());
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread[]ts=new Thread[10];
//建立10个线程
for(int k=0;k<10;k++){
ts[k] = new Thread(new AddThread());
}
//开启10个线程
for(int k=0;k<10;k++){
ts[k].start();
}
//等待全部线程执行完成
for(int k=0;k<10;k++){
ts[k].join();
}
//打印最终执行结果
System.out.println(arr);
}
}复制代码
执行结果:
[10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000]复制代码
示例三:原子方式更新引用
public class Node {
private int val;
private volatile Node left, right;
private static final AtomicReferenceFieldUpdater leftUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "left");
private static AtomicReferenceFieldUpdater rightUpdater = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "right");
boolean compareAndSetLeft(Node expect, Node update) {
return leftUpdater.compareAndSet(this, expect, update);
}
public Node() {
this.left = this.right = null;
}
public Node(int val) {
this.val = val;
this.left = this.right = null;
}
public Node(Node left,Node right) {
this.left = left;
this.right = right;
}
public static void main(String[] args) {
Node node = new Node(1);
node.left = new Node(new Node(2),new Node(3));
node.right = new Node(new Node(4),new Node(5));
System.out.println(JSON.toJSON(node));
node.compareAndSetLeft(node.left,node.right);
System.out.println(JSON.toJSON(node));
}
// get and set ...
}复制代码
执行结果:
{"val":1,"left":{"val":0,"left":{"val":2},"right":{"val":3}},"right":{"val":0,"left":{"val":4},"right":{"val":5}}}
{"val":1,"left":{"val":0,"left":{"val":4},"right":{"val":5}},"right":{"val":0,"left":{"val":4},"right":{"val":5}}}复制代码
(4)小结
原子访问和更新的内存效果通常遵循如下可变规则中的声明:
关键源码:
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();复制代码
查看源码发现Atomic包里的类基本都是使用Unsafe实现的,Unsafe只提供了如下三种CAS方法。
关键源码:
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);复制代码
查看方法不难发现是被native修饰的,即被Native修饰的方法在被调用时指向的是一个非java代码的具体实现,这个实现多是其余语言或者操做系统。这里是借助C来调用CPU底层指令来实现的,具体实现原理请点击下面的“实现原理”。
原子变量类主要用做各类构造块,用于实现非阻塞数据结构和相关的基础结构类。compareAndSet方法不是锁的常规替换方法。仅当对象的重要更新限定于单个变量时才应用它。
例:多线程高并发计数器
原子变量类相对于基于锁的版本有几个性能优点。首先,它用硬件的原生形态代替 JVM 的锁定代码路径,从而在更细的粒度层次上(独立的内存位置)进行同步,失败的线程也能够当即重试,而不会被挂起后从新调度。更细的粒度下降了争用的机会,不用从新调度就能重试的能力也下降了争用的成本。即便有少许失败的 CAS 操做,这种方法仍然会比因为锁争用形成的从新调度快得多。