首先我们要了解Java内存模型(Java Memory Model)。JMM就是一套规范,描述了Java线程对变量的访问规则。
首先,在jvm中有一个main memory,而每个线程都有自己的working memory,一个线程对一个variable进行操作的时候,会先在自己的working memory里面建立一个copy,操作完成之后再写入main memory,如果有多个线程同时操作同一个variable,就可能会出现不可预知的结果,所以线程安全就是为了避免这种情况的发生。
volatile是不错的机制,volatile关键字作用:1.volatile可以保证变量的可见性。2.保证有序性。(防止指令重排)。【当一个线程修改了共享变量的值,新的值会立刻同步到主内存当中。而其他线程读取这个变量的时候,也会强制从主内存中拉取最新的变量值。】但是volatile不能保证原子性。因此对于同步最终还是要回到锁机制上来。在java中,确保线程安全的方法有两种:一种是使用内置锁(synchronized),一种是使用原子类(java.util.concurrent包下的),对于原子类,它所用的机制就是CAS机制(Compare And Swap)。
在Doug Lea提供的cucurenct包中,CAS理论是它实现整个java包的基石。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。
CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”CAS是通过无限循环来获取数据的,若果在第一轮循环中,a线程获取地址里面的值被b线程修改了,那么a线程需要自旋,到下次循环才有可能机会执行。
通常将 CAS 用于同步的方式是从地址 V 读取值 A,执行多步计算来获得新 值 B,然后使用 CAS 将 V 的值从 A 改为 B。如果 V 处的值尚未同时更改,则 CAS 操作成功。利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。
现代cpu提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而compareAndSet()就是用这些代替了锁定,compareAndeSet()是调用本地方法来完成cpu指令的操作。
来看看AutomicInteger的源码:
private volatile int value;//毫无疑问,没有锁的机制下,必须借助volatile保证线程间的数据可见性
public final int get(){
return value;
}
// 来看看++i是怎么实现的:
public final int increamentAndGet(){
for(;;){ // 无限循环来获取数据
int current = get();
int next = current + 1;
if(compareAndSet(current,next))
return next;
}
}