线程安全之原子性--Atomic包-Part-1

概览

线程安全
  • 原子性:提供了互斥访问,同一时刻只能有一个线程来对它进行操作;
  • 可见性:一个线程对主内存的修改可以及时地被其他线程知道;
  • 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

    使用AtomicInteger类

    之前当使用int时,根据结果知道,该操作是非线程安全的。
    当使用AtomicInteger类时,并使用count.incrementAndGet();而不是count++;后,多次验证其结果为:

图

为什么使用了count的incrementAndGet()方法后就变成线程安全的了呢?

查看源码!
count.incrementAndGet():

1
2
3
4
5
6
7
8
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

其中使用到unsafe的getAndAddInt(xxx)方法!!

那么这个方法是何方神圣?
看看它的实现:

1
2
3
4
5
6
7
8
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

可以看出它使用do-while语句实现,其实不止getAndAddInt用到,其他的get(或set)AndAddxxx(Object)都是一样的实现方式。其中其判断的条件又用到unsafe类的compareAndSwapInt(xxx)方法,再点进去看看!!

1
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

就这一句没了……

但是!!!

我看到方法类型中有一个字段native!!!
这说明该方法是JNI框架(Java Native Interface,Java本地接口)的方法。

再Google一下:

在编程领域, JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。本地程序一般是用其它语言(C、C++或汇编语言等)编写的, 并且被编译为基于本机硬件和操作系统的程序。
JNI框架允许Native方法调用Java对象,就像Java程序访问Native对象一样方便。Native方法可以创建Java对象,读取这些对象, 并调用Java对象执行某些方法。当然Native方法也可以读取由Java程序自身创建的对象,并调用这些对象的方法。

JNI在某些情况下可能带来很大的开销和性能损失:

  • 调用 JNI 方法是很笨重的操作, 特别是在多次重复调用的情况下.
  • Native 方法不会被 JVM 内联, 也不会被 JIT compiled 优化 , 因为方法已经被编译过了.
  • Java 数组可能会被拷贝一份,以传递给 native 方法, 执行完之后再拷贝回去. 其开销与数组的长度是线性相关的.
  • 如果传递一个对象给方法,或者需要一个回调,那么 Native 方法可能会自己调用JVM。 访问Java对象的属性、方法和类型时, Native代码需要类似reflection的东西。签名由字符串指定,通从JVM中查询。这非常缓慢并且容易出错。
  • Java 中的字符串(String) 也是对象, 有 length 属性,并且是编码过的. 读取或者创建字符串都需要一次时间复杂度为 O(n) 的复制操作.
再分析其函数的调用:
1
2
3
4
5
6
7
8
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

return var5;
}

其中var1为当前传入的对象即count,var2是当前count的值,var4即为incrementAndGet()方法的默认加数,为1,var5是调用的另一个JNI的方法得到底层当前的count值。

1
public native int getIntVolatile(Object var1, long var2);

Volatile关键字很熟悉,简述是保证了线程之间操作的对象的值互相透明、可见性。
(Volatile关键字的详述又该是一个大文章了!过阵子再写!)
那么现在应该就懂了,用底层得到的值不断循环比较当前的值,如果两者相等就加一并再用JNI底层方法写入主内存。
AtomicInteger类就是使用了该方法即CAS原理来保证线程安全的。(CAS原理也是一个大块头。。)
同理,AtomicXXX类(XXX是属于Java的Object,例如Long、Double等)都是该方法保证的!

ConpareAndSet概述

该方法和ConpareAndSwap有些相似,但还有不同:ConpareAndSet(a,b)更多的是用在AtomicBoolean类中(Boolean要求false与true的转换同一时间只能被一个线程调用,且false(true)只能转换为true(false),其中的参数a,b即为只能相互转换的数,顺序为:当为a的时候更新为b)。

SupriseMF wechat
欢迎关注微信订阅号【星球码】,分享学习编程奇淫巧技~
喜欢就支持我呀(*^∇^*)~

热评文章