Java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了
一种用法简单、性能高效、线程安全地更新一个变量的方式,在JDK1.8中,有17个类
这些类的大概用途:
Atomic提供了三个类
首先看AtomicInteger类。
下面看看getAndIncrement()方法
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
可以看到,底层还是调用了Unsafe的getAndAddInt方法,继续看getAndAddInt方法
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!CompareAndSwapInt(o, offset, v, v + delta));
return v;
}
因为CAS是“无锁”的基础,它允许更新失败。所以经常会与while循环搭配,在失败后不断去重试。
v是要返回的值,即该方法返回的是原来的值,而新值是v+delta。这里使用了do-while循环,它的目的是保证循环体内的语句至少会被执行一遍。这样才能保证return的值v是我们期望的值。至于为什么要把获取“旧值”的操作放到循环体内呢?因为CAS如果旧值V不等于预期值E,它就会更新失败,说明旧值发生了变化。那我们当然需要返回的是被其他线程改变之后的值,因此放到了do循环体内。
下面看一个具体例子
package com.cxylk.thread.atomic;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Classname AtomicIntegerDemo
* @Description TODO
* @Author likui
* @Date 2020/12/6 22:42
**/
public class AtomicIntegerDemo {
private static final Unsafe unsafe;
private static final long stateOffset;
private volatile long state=1;
private static AtomicInteger integer=new AtomicInteger();
static {
Field field= null;
try {
field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe= (Unsafe) field.get(null);
stateOffset=unsafe.objectFieldOffset(AtomicIntegerDemo.class.getDeclaredField("state"));
} catch (Exception e) {
throw new Error(e);
}
}
public static void main(String[] args) {
// System.out.println(integer.incrementAndGet());
//这里调用到的Unsafe中getAndAddInt方法中的原始值是AtomicInteger中的value
// System.out.println("返回旧值"+integer.getAndIncrement());//输出0,先get再increment
// System.out.println("获取当前值"+integer.get());//这时候的值就是加1后的值
System.out.println("========================");
System.out.println("返回新值"+integer.incrementAndGet());//返回加1后的值
System.out.println("获取当前值"+integer.get());//这时候获取的值就是返回的值
//这里传过去的原始值就是该类中定义的state
int result=get(new AtomicIntegerDemo(),stateOffset,1);
System.out.println(result);
}
/**
* 实现Unsafe类中的getAndAddInt方法,获取对象obj中偏移量为offset的变量对应volatile语义的当前值,
* 并设置变量值为原始值+addValue
* @param obj 所要操作的对象
* @param offset 在对象中的偏移地址
* @param addValue 增量
* @return 注意,这里返回的是当前变量的值,不是原始值+addValue
*/
public static int get(Object obj,long offset,int addValue){
int var5;
do{
//在该类中,这里获取的就是state值,所以,state初始值大小决定了这里的变量值
//也就是说,这里的变量值就是原始变量值
var5=unsafe.getIntVolatile(obj,offset);
}while (!unsafe.compareAndSwapInt(obj,offset,var5,addValue+var5));
return var5;
}
}
AtomicLong的基本用法
package com.cxylk.thread.atomic;
import java.util.concurrent.atomic.AtomicLong;
/**
* @Classname AtomicLongDemo
* @Description 通过一个多线程统计0的个数加深对AtomicLong原子变量操作类的理解
* @Author likui
* @Date 2020/12/6 21:37
**/
public class AtomicLongDemo {
//创建Long型原子计数器
private static AtomicLong atomicLong=new AtomicLong();
//创建数组存放数据
private static Integer[] arrayOne=new Integer[]{0,1,2,3,0,5,6,0,56,0};
private static Integer[] arrayTwo=new Integer[]{10,1,2,3,0,5,6,0,45,0};
public static void main(String[] args) throws InterruptedException {
//线程one统计arrayOne中0的个数
Thread threadOne=new Thread(()->{
int size=arrayOne.length;
for (int i = 0; i < size; i++) {
if(arrayOne[i].intValue() ==0){
//自增后的值,从名字上也可以看出,先increment,再get
atomicLong.incrementAndGet();
}
}
},"线程one");
//线程two统计arrayTwo中0的个数
Thread threadTwo=new Thread(()->{
int size=arrayTwo.length;
for (int i = 0; i < size; i++) {
if(arrayTwo[i].intValue()==0){
atomicLong.incrementAndGet();
}
}
},"线程two");
//启动子线程
threadOne.start();
threadTwo.start();
//等待线程执行完毕,这里一定要加上这步,防止main线程不等子线程执行完就返回,0的个数将是0
threadOne.join();
threadTwo.join();
System.out.println("0的个数:"+atomicLong.get());
}
}
通过原子方式更新数组里的某个元素,主要有4个类
几个类的方法大同小异,主要学习AtomicIntegerArray的常用方法
先看看构造函数
private final int[] array;
/**
* Creates a new AtomicIntegerArray of the given length, with all
* elements initially zero.
*
* @param length the length of the array
*/
public AtomicIntegerArray(int length) {
array = new int[length];
}
/**
* Creates a new AtomicIntegerArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
两个构造方法:
具体例子
package com.cxylk.thread.atomic;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* @Classname AtomicIntegerArrayDemo
* @Description 原子更新整型数组里的元素
* @Author likui
* @Date 2020/12/7 21:37
**/
public class AtomicIntegerArrayDemo {
private static int[] value=new int[]{1,2,3};
//当构造函数传入值的时候,AtomicIntegerArray会将当前值复制一份 this.array=value.clone()
private static AtomicIntegerArray ai=new AtomicIntegerArray(value);
public static void main(String[] args) {
System.out.println(ai.getAndSet(0, 3));//输入的是原始值
System.out.println(ai.get(0));//这时候索引0的值变为了3
//上面说了当前传进去的value被复制了一份,所以当AtomicIntegerArray
//对内部元素进行修改时,不会影响传入的数组
System.out.println(value[0]);//还是1
}
}
其中的getAndSet源码如下
/**
* Atomically sets the element at position {@code i} to the given
* value and returns the old value.
*
* @param i the index
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int i, int newValue) {
return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
}
可以看到,内部还是调用了Unsafe的getAndSetInt方法,其中checkdByteOffset(i)对参数进行校验
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
}
原子更新基本类型只能更新单个变量,当要对多个变量进行原子更新时就要使用这个原子更新引用类型提供的类。Atomic提供以下3个类
主要看一下AtomicReference类的使用
看下该类中的字段和构造方法及一些常用方法
//value在该类中的偏移量
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//valatile保证可见性
private volatile V value;
/**
* Creates a new AtomicReference with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicReference(V initialValue) {
value = initialValue;
}
/**
* Creates a new AtomicReference with null initial value.
*/
public AtomicReference() {
}
package com.cxylk.thread.atomic;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Classname AtomicReferenceDemo
* @Description 原子更新引用类型。原子更新基本类型AtomicInteger只能更新一个变量,如果
* 需要原子更新多个变量,就需要使用原子更新应用类型提供的这个类
* @Author likui
* @Date 2020/12/7 22:37
**/
public class AtomicReferenceDemo {
//默认构造函数,此时AtomicReference中的User value=null
static AtomicReference<User> atomicReference=new AtomicReference<User>();
public static void main(String[] args) {
User user=new User("likui","20");
//此时value=user
atomicReference.set(user);
User newUser=new User("cxylk","22");
//更新值,更新之后的value=newUser
atomicReference.compareAndSet(user,newUser);
//返回更新后的值,即newUser
System.out.println(atomicReference.get().getName());
System.out.println(atomicReference.get().getAge());
}
static class User{
private String name;
private String age;
public User(String name,String age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
}
输出结果:cxylk,22
如果需要原子更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了一下3个类进行原子字段更新
下面以AtomicIntegerFieldUpdater进行演示
先来看看该类源码
public abstract class AtomicIntegerFieldUpdater<T> {
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
.....
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
* the same as tclass
*/
private final Class<?> cclass;
/** class holding the field */
private final Class<T> tclass;
AtomicIntegerFieldUpdaterImpl(final Class<T> tclass,
final String fieldName,
final Class<?> caller) {
final Field field;
final int modifiers;
try {
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) &&
((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
if (field.getType() != int.class)
throw new IllegalArgumentException("Must be integer type");
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
// Access to protected field members is restricted to receivers only
// of the accessing class, or one of its subclasses, and the
// accessing class must in turn be a subclass (or package sibling)
// of the protected member's defining class.
// If the updater refers to a protected field of a declaring class
// outside the current package, the receiver argument will be
// narrowed to the type of the accessing class.
this.cclass = (Modifier.isProtected(modifiers) &&
tclass.isAssignableFrom(caller) &&
!isSamePackage(tclass, caller))
? caller : tclass;
this.tclass = tclass;
this.offset = U.objectFieldOffset(field);
}
.....
首先可以看到该类是抽象类,所以外部类想要获取到该类的实例,每次使用的时候必须使用静态方法newUpdater()创建一个更新器i。而且由于有些类型的字段是不可被更新的,所以被更新的字段有很多要求,不满足要求的话会抛出各种异常。下面具体探讨
package com.cxylk.thread.atomic;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* @Classname AtomicIntegerFieldUpdaterDemo
* @Description 原子更新字段类AtomicIntegerFieldUpdater测试
* @Author likui
* @Date 2020/12/8 9:39
**/
public class AtomicIntegerFieldUpdaterDemo {
static AtomicIntegerFieldUpdater<User> aifu=AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
public static void main(String[] args) {
User user=new User("lk","20");
//得到的是原始值
System.out.println(aifu.getAndIncrement(user));
//获取更新后的值
System.out.println(aifu.get(user));
}
static class User{
private String name;
private String age;
public User(String name,String age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public String getAge() {
return age;
}
}
}
上面例子中,我想要更新User类中的age字段,让它加1,但是程序运行时将会抛出异常
Caused by: java.lang.RuntimeException: java.lang.IllegalAccessException: Class com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo can not access a member of class com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo$User with modifiers "private"
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:405)
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
at com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo.<clinit>(AtomicIntegerFieldUpdaterDemo.java:12)
Caused by: java.lang.IllegalAccessException: Class com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo can not access a member of class com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo$User with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at sun.reflect.misc.ReflectUtil.ensureMemberAccess(ReflectUtil.java:103)
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:394)
... 2 more
Exception in thread "main"
1.大概意思就是当前类不能访问被private修饰的age字段
对应源码中的静态内部类
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
其中field返回一个age的全名,modifiers得到该字段修饰符代表的具体数值,各修饰符对应的具体数值如下
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
如果是public volatile,那么modifiers=65
所以这里的modifiers=2
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) &&
((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
上面进行一个访问权限的判定,这里不做研究,最终抛出上面的异常
2.将age改为public修饰
public String age;
运行程序,报错
Caused by: java.lang.IllegalArgumentException: Must be integer type
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:409)
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
at com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo.<clinit>(AtomicIntegerFieldUpdaterDemo.java:12)
Exception in thread "main"
可以看到,类型必须时integer类型
源码中对应位置
if (field.getType() != int.class)
throw new IllegalArgumentException("Must be integer type");
那么将其改为Integer类型,仍然抛出上述异常。原因上面源码说的很清除了,只能修改ing类型的字段,不能修改其包装类型,如果要修改其包装类型,使用AtomicReferenceFieldUpdater。对于AtomicLongFieldUpdater也是一样的。
3.将其改为int 类型,运行程序,报错
Caused by: java.lang.IllegalArgumentException: Must be volatile type
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:412)
at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
at com.cxylk.thread.atomic.AtomicIntegerFieldUpdaterDemo.<clinit>(AtomicIntegerFieldUpdaterDemo.java:12)
4.说改字段必须时volatile类型的,对应源码位置
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
改为volatile修饰
public volatile int age;
运行结果:20 21
5.如果给字段加static关键字也会报错
Caused by: java.lang.IllegalArgumentException
at sun.misc.Unsafe.objectFieldOffset(Native Method)
原因就是objectFieldOffset获取的改字段在对象中的偏移地址,而static修饰的变量属于类变量
最后,做个总结:
上面说了AtomicInterFileldUpdater只能更新int类型的字段,下面用AtominReferenceFieldUpdater更新Integer字段
package com.cxylk.thread.atomic;
import sun.misc.Unsafe;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* @Classname AtomicReferenceFieldUpdaterDemo
* @Description 原子更新引用类型中的字段,解决AtomicIntegerFieldUpdater或者LongUpdater只能更新
* int和long类型字段的问题
* @Author likui
* @Date 2020/12/8 11:02
**/
public class AtomicReferenceFieldUpdaterDemo {
//第一个值是要更新字段所再的类,第二个是该字段的类型,第三个是字段名
private static AtomicReferenceFieldUpdater<User,Integer> arfu=
AtomicReferenceFieldUpdater.newUpdater(User.class,Integer.class,"age");
public static void main(String[] args) {
User user=new User("likui",20);
User newUser=new User("cxylk",22);
arfu.compareAndSet(user,user.age,newUser.age);
System.out.println(user.age);
}
static class User{
private String name;
public volatile Integer age;
public User(String name,int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
用法基本一样,注意字段仍然是public volatile,构造函数传值也有点区别
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。