1. 简述
JUC 并发包中包含有 Atomiclnteger 、AtomicLong 和 AtomicBoolean 等原子性操作类 ,它们的原理类似
以AutomicLong为例:AtomicLong是原子性递增或者递减类,其内部使用Unsafe来实现
public class AtomicLong extends Number implements java.io.Serializable {
private static final long serialVersionUID = 1927816293512124184L;
// 1. 获取Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//2. 存放变量value的偏移量
private static final long valueOffset;
//3. 判断JVM是否支持Long类型无锁CAS
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportsCS8();
static {
try {
//4. 获取value在AtomicLong中的偏移量
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile long value;//实际变量值
public AtomicLong(long initialValue) {
value = initialValue;
}
......
为什么AutomicLong类中可以获得Unsafe的实例?
因为 AtomicLong类也是在 rt.jar 包下面的,AtomicLong 类就是通过 BootStarp 类加载器进行加载的
2. 常用方法
1. 递增递减操作
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
//调用unsafe方 法, 原子性设置value值为原始值+1 ,返回值为递增后的值
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
/**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
//调用 unsafe方法,原子性设置 value值为原始值-1 ,返回值为递减之后的值
public final long decrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
//调用unsafe方法,原子性设置value值为原始值+1, 返回值为原始值
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
// //调用unsafe方法,原子性设置value值为原始值-1, 返回值为原始值
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
如上代码内部都是通过调用 Unsafe getAndAddLong方法来实现操作,这个函数是个原子性操作,这里第一个参数是 AtomicLong 实例的引用 ,第二个参数是 value 变量在 AtomicLong 中的偏移值,第三个参数value要递增或递减的值
public final long getAndAddLong(Object paramObject , long paramLong1 , long
paramLong2)
long l ;
do
l = getLongvolatile(paramObject , paramLongl) ;
) while (!compareAndSwapLong(paramObject , paramLong1 , l, l + paramLong2) );
return l ;
//如果当前数值是l(期待原子变量值是l) 则将原子变量值更新为l+paramLong2(如果在更新之前有其他的线程增加了原子变量值,则此时不相等
//继续CAS比较)
}
2. compareAndSet(long expect, long update)方法
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
//如果原子变量中的value值等于expect,则使用 update值更新该值并返回true , 否则返回false
3. 代码实践
package innerlock;
import java.util.concurrent.atomic.AtomicLong;
public class AutomicLongDemo {
private static AtomicLong count=new AtomicLong();//count初值为0
private static int []arr1= {0,1,2,3,0,1,2,3,0,0};//4个0
private static int []arr2= {1,0,2,0,5,0,6,7,0,9};//4个0
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread() {
public void run() {
int sz=arr1.length;
for(int i=0;i<sz;i++)
{
if(arr1[i]==0)
count.incrementAndGet();
}
}
};
Thread t2=new Thread() {
public void run() {
int sz=arr2.length;
for(int i=0;i<sz;i++)
{
if(arr2[i]==0)
count.incrementAndGet();
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();//等待t1 t2线程执行结束
System.out.println("0的个数:"+count.get());
}
}
上述代码使用两个线程分别统计arr1和arr2中的0的个数,在没有原子类的情况下,
实现计数器需要使用一定的同步措施,比如使用synchronized 关键字等 , 但是这些都是阻塞算法 ,
对性能有一定损耗 ,而原子操作类都使用 CAS 非阻塞算法 ,性能更好。但是在高并发情况下 AtomicLong 还会存在性能问题 。