网站首页 > java教程 正文
JUC并发包中有AtomicInteger、AtomicIntegerArray、AtomicLong、AtomicBoolean等原子变量操作类,他们原理都类似,本文主要分析为什么需要原子操作类以及AtomicLong类的实现原理。
为什么需要原子变量操作类?
public class Test {
private static long count = 0L;
private static void increase() {
count++;
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 1000; j++) {
increase();
}
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println(count);
}
}
上方的代码创建一个普通long类型的变量count,启动10个线程分别对count进行1000次递增操作。我们知道count++解析为count=count+1,这个操作并不是原子性的,多线程执行这个操作必然会出现问题,如果按照正常的并发情况,执行结果应该是10000,但是实际情况是每次执行的结果都是小于10000的数字。为什么呢?
有人肯定会说,使用volatile来修饰count变量是否能解决这个问题,这里顺便来说下volatile关键字的作用及使用场景。
volatile作用
- 当一个共享变量被volatile修饰时,它会保证修改的值立即被更新到主存,通俗来讲就是,线程A对一个volatile变量的修改,对于其它线程来说是可见的,即线程每次获取volatile变量的值都是最新的
- 禁止指令重排序
volatile使用场景
- 对变量的写操作不依赖当前值
- 该变量没有包含在具有其它变量的不变式中
private static volatile long count = 0L;
用volatile关键字修饰count后再次测试,发现执行结果依然小于10000。
分析volatile使用场景发现必须满足对变量的写操作不应该依赖当前值,而count++明显是依赖当前值的,所以多线程下执行count++是无法通过volatile保证结果准确性的。
那既然无法通过volatile关键字来保证准确性,我们就可以尝试使用AtomicLong原子变量操作类来进行操作,代码如下
public class Test {
private static AtomicLong count = new AtomicLong(0L);
private static void increase() {
count.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < 1000; j++) {
increase();
}
}
});
threads[i].start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println(count);
}
}
我们发现不管怎么执行,最后的结果都是10000,这是因为AtomicInteger.incrementAndGet()方法是原子性的。
AtomicLong原理分析
首先,我们来看一下一下AtomicLong的属性
public class AtomicLong extends Number implements java.io.Serializable {
//通过Unsafe.getUnsafe()方法,获取unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
//存放变量value的偏移量
private static final long valueOffset;
//判断JVM是否支持Long类型无锁CAS
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
//VMSupportsCS8()方法是native方法
private static native boolean VMSupportsCS8();
//静态代码块获取value在AtomicLong中的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//实际使用的变量值,可以看到这里也是使用volatile修饰的,保证多线程下的内存可见性
private volatile long value;
//构造函数
public AtomicLong(long initialValue) {
value = initialValue;
}
public AtomicLong() {
}
}
然后我们来看下AtomicLong的主要方法,都是使用Unsafe来实现的
//调用unsafe方法原子性设置value的值,并返回设置之前的值
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
//getAndSetLong()方法是使用CAS方式设置value值的
//其中getLongVolatile()方法和compareAndSwapLong()方法是native方法
public final long getAndSetLong(Object var1, long var2, long var4) {
long var6;
//每个线程获取到变量的当前值,然后使用CAS方式设置新的值,如果设置失败,则循环继续尝试,直到成功为止
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var4));
return var6;
}
//如果当前值等于期望值,则原子性设置value为新的值
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
//调用unsafe方法,原子性设置当前值自增1,返回修改前的值
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
//调用unsafe方法,原子性设置当前值自减1,返回修改前的值
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
//调用unsafe方法,原子性设置当前值加上参数值,返回修改前的值
public final long getAndAdd(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta);
//调用unsafe方法,原子性设置当前值自增1,返回修改后的值
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
//调用unsafe方法,原子性设置当前值自减1,返回修改后的值
public final long decrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
//调用unsafe方法,原子性设置当前值加上参数值,返回修改后的值
public final long addAndGet(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
可以看到,AtomicLong的操作都是通过Unsafe类来实现的
- 上一篇: Java多线程之原子性、可见性与有序性
- 下一篇: 并发编程之CAS(并发编程是啥意思)
猜你喜欢
- 2025-07-19 Java AtomicInteger操作详解(java attachment)
- 2025-07-19 无锁同步-JAVA之Volatile、Atomic和CAS
- 2025-07-19 Java 作用域详解:从变量可见性到代码封装
- 2025-07-19 CompletableFuture源码分析(fragment源码分析)
- 2025-07-19 Java并发编程从入门到进阶 多场景实战
- 2025-07-19 一文搞懂 CAS 操作与 ABA 问题:高并发编程的关键知识点
- 2025-07-19 Java 内存模型、JVM 内存结构(java的内存模型)
- 2025-07-19 Java面试必备八股文(java面试必备八股文河北人社)
- 2025-07-19 Java面试都在问的CAS,你还没掌握吗?
- 2025-07-19 java VarHandle介绍(java variant)
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)