在java中一般为了保护多线程之间共享数据的安全,我们都是用加锁(Lock)或者synchronized机制,锁的机制就像“门”一样,一般情况下,只能让一个线程进行访问,其他线程必须进入等待,相当于独占的方式访问,现代的许多的jvm都对非竞争锁的获取和锁的释放等操作进行了极大的优化,但如果有多个线程同时请求锁,那么jvm就需要借助操作系统的功能。将一些线程挂起,等待其他其他线程执行完后又要将挂起的线程恢复运行。在挂起和恢复线程的过程中存在很大的开销。并且如果锁粒度大的话,其他线程必须等待很长的时候,如果锁粒度小的话就会造成过度切换的资源浪费 。
近年来,很多非阻塞算法和原子变量都可以非常高效的解决进程之间的共享问题。它们具有很好的可伸缩性和活跃性。非阻塞式算法不仅可以很多个线程在竞争相同数据时不会发生阻塞,而且可以解决锁机制中 常见的死锁问题,休眠,自旋问题。原子变量 是类似于“volatile 类型变量” 变量的所有方法都是安全的,可以实现计数器,序列发生器,统计数据收集。
比较并交换CAS 这是一种基于处理器架构的指令,CAS包含了3个操作数--需要读写的内存位置V,进行比较的值A和拟定入的新值B,当且仅当V的值等于A时,CAS才会通过原子方式用新值B来更新V的值,否则不会执行任何操作。无论位置V的值是否等于A,都将返回V原有的值,CAS可以简单的说:我认为V的值应该是A,如果是我就进行设置,否则不修改并告诉V的实际值。CAS是一种乐观的技术,它希望能成功地执行更新操作,并且如果有另一个线程在最近一次检查后更新了该变量,那么CAS能检测到这个错误,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量成功,其他线程都失败,然后,失败的线程并不会被挂起,与锁的机制有很大的不同,因为获取锁失败后的线程会被挂起,如果一个线程CAS失败不会阻塞,因此安可以决定是否重新尝试,或者执行一些恢复操作,也或者不执行任何操作,这大大降低了死锁的出现。
下面我们来看一下用Unsafe实现的CAS操作:
package javaThread;
import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import sun.misc.Unsafe;
class ShareNoBlackData {
public volatile int value = 100;
}
public class NoBlackSharedData extends ShareNoBlackData {
private static final Unsafe THE_UNSAFE;
static {
try {
final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
public Unsafe run() throws Exception {
Field theUnsafe = Unsafe.class
.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (Unsafe) theUnsafe.get(null);
}
};
THE_UNSAFE = AccessController.doPrivileged(action);
} catch (Exception e) {
throw new RuntimeException("Unable to load unsafe", e);
}
}
private static long VALUE_OFFSET;
public NoBlackSharedData() {
try {
VALUE_OFFSET = THE_UNSAFE.objectFieldOffset(ShareNoBlackData.class
.getDeclaredField("value"));
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
public int getData() {
return value;
}
public synchronized int finData(int i) {
//return value.getAndAdd(i);
int newvalue ;
int oldvalue ;
do {
oldvalue =getData();
newvalue = oldvalue + i;
} while(!THE_UNSAFE.compareAndSwapInt(this, VALUE_OFFSET, oldvalue, newvalue)) ;
//value = value + i;
return newvalue;
}
}
初看可能会感觉CAS性能上面会差一些,因为这需要执行更多的操作和更复杂的控制流,还依赖看似复杂的CAS操作。但如果你经常测试,你会发现它比基于锁的实现具有更好的性能。