java中原子变量之AtomicLong

转自:https://leokongwq.github.io/2016/12/31/java-AtomicLong.html

在阅读java并发编程实践时,其中有一个章节讲到了原子变量。这是原子变量类型是在jdk1.5由大神Doug Lea编写的,能够对高并发操作提供良好的支持并且java.util.concurrent包中的需要并发工具也是基于这些原子变量实现。看过的东西过一段时间就不够清晰了,今天重新学习一下这些原子变量的内容。只以AtomicLong举例,其它的都是类似的。

AtomicLong介绍

jdk的文档说AtomicLong是一个代表能别原子化更新的long值。AtomicLong可以在应用中作为一个序列号生成器来使用。就以这个应用场景将AtomicLong是如何实现原子化更新的。

AtomicLong生成序列号

AtomicLong seqNum = new AtomicLong(1);
long id = seqNum.getAndIncrement();

这两行代码分别创建了一个初始值为1的AtomicLong的变量并调用了它的getAndIncrement获取它当前的值并自增1。

AtomicLong的getAndIncrement方法是不会有线程安全问题的。解释如下:

1
2
3
4
5
6
7
8
9
/**
 * Atomically increments by one the current value.
 * 原子的将当前的值加一
 * @return the previous value 
 * 返回当前的值
 */
public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

上面代码的关键是unsafe变量, 这个变量是干嘛的呢?

Unsafe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
```    

简单来说Unsafe就是一个不安全的类,它包含了许多可以执行底层操作的不安全方法,即使说该类和它的方法都是public的。使用该类是受限的,只有受信任的代码才可以使用它。想下面这样使用就会报异常:

```java
Unsafe unsafe = Unsafe.getUnsafe();
try {
    final long valueOffset = unsafe.objectFieldOffset(AtomicLong.class.getDeclaredField("value"));
} catch (NoSuchFieldException e) {
    e.printStackTrace();
}

解决的办法有2个:

  1. 让虚拟机信任我们的代码

    java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient
    
  2. 使用反射

    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);         
    

Unsafe类的详细文档说明可以参考http://www.docjar.com/docs/api/sun/misc/Unsafe.html

unsafe.getAndAddLong

在AtomicLong的方法中用到了unsafe.getAndAddLong, 该方法最终会调用

1
2
3
4
5
6
7
8
 /**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapLong(Object o, long offset,
                                              long expected,
                                              long x);

可以看到这是一个native方法,更底层是通过CPU支持的类似的指令来实现的。所以比我们一般使用的锁效率要高很多。

你可能感兴趣的:(java中原子变量之AtomicLong)